diff --git a/app/playlists.py b/app/playlists.py index 7f56d9b..1090720 100644 --- a/app/playlists.py +++ b/app/playlists.py @@ -53,7 +53,7 @@ if TYPE_CHECKING: from musicmuster import Window -class TextDelegate(QStyledItemDelegate): +class PlaylistDelegate(QStyledItemDelegate): """ - closes the edit on control-return - checks with user before abandoning edit on Escape @@ -92,11 +92,13 @@ class TextDelegate(QStyledItemDelegate): parent: Optional[QWidget], option: QStyleOptionViewItem, index: QModelIndex, - ) -> Optional[QTextEdit]: + ) -> Optional[QDoubleSpinBox | QTextEdit]: """ Intercept createEditor call and make row just a little bit taller """ + editor: QDoubleSpinBox | QTextEdit + class Editor(QTextEdit): def resizeEvent(self, event): super().resizeEvent(event) @@ -108,12 +110,17 @@ class TextDelegate(QStyledItemDelegate): if self.current_editor: editor = self.current_editor else: - editor = Editor(parent) - editor.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) - editor.setFrameShape(QFrame.Shape.NoFrame) - self.current_editor = editor - - TextDelegate.EditorDocument(editor) + if index.column() == Col.INTRO.value: + editor = QDoubleSpinBox(parent) + editor.setDecimals(1) + editor.setSingleStep(0.1) + return editor + elif isinstance(index.data(), str): + editor = Editor(parent) + editor.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + editor.setFrameShape(QFrame.Shape.NoFrame) + self.current_editor = editor + PlaylistDelegate.EditorDocument(editor) return editor def destroyEditor(self, editor: Optional[QWidget], index: QModelIndex) -> None: @@ -174,9 +181,12 @@ class TextDelegate(QStyledItemDelegate): elif key == Qt.Key.Key_Escape: # Close editor if no changes have been made - if hasattr(editor, "toPlainText"): + data_modified = False + if isinstance(editor, QTextEdit): + data_modified = self.original_model_data.value() != editor.toPlainText() + elif isinstance(editor, QDoubleSpinBox): data_modified = ( - self.original_model_data.value() != editor.toPlainText() + self.original_model_data.value() != int(editor.value()) * 1000 ) if not data_modified: self.closeEditor.emit(editor) @@ -211,7 +221,8 @@ class TextDelegate(QStyledItemDelegate): edit_index, Qt.ItemDataRole.EditRole ) if index.column() == Col.INTRO.value: - editor.setValue(self.original_model_data.value() / 1000) + if self.original_model_data.value(): + editor.setValue(self.original_model_data.value() / 1000) else: editor.setPlainText(self.original_model_data.value()) @@ -229,229 +240,6 @@ class TextDelegate(QStyledItemDelegate): editor.setGeometry(option.rect) -# Will be int delegate -# class EscapeDelegate(QStyledItemDelegate): -# """ -# - increases the height of a row when editing to make editing easier -# - closes the edit on control-return -# - checks with user before abandoning edit on Escape -# - positions cursor where double-click occurs - -# Parts inspired by https://stackoverflow.com/questions/69113867/ -# make-row-of-qtableview-expand-as-editor-grows-in-height -# """ - -# class EditorDocument(QTextDocument): -# def __init__(self, parent): -# super().__init__(parent) -# self.setDocumentMargin(0) -# self.contentsChange.connect(self.contents_change) -# self.height = None -# parent.setDocument(self) - -# def contents_change(self, position, chars_removed, chars_added): -# def resize_func(): -# if self.size().height() != self.height: -# doc_size = self.size() -# self.parent().resize(int(doc_size.width()), int(doc_size.height())) - -# QTimer.singleShot(0, resize_func) - -# def __init__(self, parent: QWidget, source_model: PlaylistModel) -> None: -# super().__init__(parent) -# self.source_model = source_model -# self.signals = MusicMusterSignals() -# self.click_position = None # Store the mouse click position -# self.current_editor = None - -# def createEditor( -# self, -# parent: Optional[QWidget], -# option: QStyleOptionViewItem, -# index: QModelIndex, -# ) -> Optional[QDoubleSpinBox | QTextEdit]: -# """ -# Intercept createEditor call and make row just a little bit taller -# """ - -# # editor: QDoubleSpinBox | QTextEdit - -# class Editor(QTextEdit): -# def resizeEvent(self, event): -# super().resizeEvent(event) -# parent.parent().resizeRowToContents(index.row()) - -# def keyPressEvent(self, event): -# if event.modifiers() == Qt.KeyboardModifier.ControlModifier: -# if event.key() == Qt.Key.Key_Return: -# print("control-return") -# import pdb; pdb.set_trace() -# self.commit.emit(self) -# # self.closeEditor.emit(editor) -# return -# elif event.key() == Qt.Key.Key_Escape: -# # Close editor if no changes have been made -# print("escape") -# data_modified = False -# if isinstance(editor, QPlainTextEdit): -# data_modified = self.original_model_data != editor.toPlainText() -# elif isinstance(editor, QDoubleSpinBox): -# data_modified = ( -# self.original_model_data != int(editor.value()) * 1000 -# ) -# if not data_modified: -# self.closeEditor.emit(editor) -# return True - -# discard_edits = QMessageBox.question( -# cast(QWidget, self.parent()), "Abandon edit", "Discard changes?" -# ) -# if discard_edits == QMessageBox.StandardButton.Yes: -# self.closeEditor.emit(editor) -# return True - -# super().keyPressEvent(event) - -# self.signals = MusicMusterSignals() -# self.signals.enable_escape_signal.emit(False) - -# # if isinstance(self.parent(), PlaylistTab): -# # p = cast(PlaylistTab, self.parent()) - -# # if index.column() == Col.INTRO.value: -# # editor = QDoubleSpinBox(parent) -# # editor.setDecimals(1) -# # editor.setSingleStep(0.1) -# # return editor -# # elif isinstance(index.data(), str): -# if self.current_editor: -# editor = self.current_editor -# else: -# editor = Editor(parent) -# editor.setVerticalScrollBarPolicy( -# Qt.ScrollBarPolicy.ScrollBarAlwaysOff -# ) -# editor.setFrameShape(QFrame.Shape.NoFrame) -# self.current_editor = editor -# # # editor.setGeometry(option.rect) # Match the cell geometry -# # row = index.row() -# # row_height = p.rowHeight(row) -# # p.setRowHeight(row, row_height + Config.MINIMUM_ROW_HEIGHT) - -# EscapeDelegate.EditorDocument(editor) -# return editor - -# # return super().createEditor(parent, option, index) - -# def destroyEditor(self, editor: Optional[QWidget], index: QModelIndex) -> None: -# """ -# Intercept editor destroyment -# """ - -# super().destroyEditor(editor, index) -# self.current_editor = None -# self.parent().resizeRowToContents(index.row()) -# self.signals.enable_escape_signal.emit(True) - -# def editorEvent( -# self, event: QEvent, model: QAbstractItemModel, option, index: QModelIndex -# ) -> bool: -# """Capture mouse click position.""" - -# if event.type() == QEvent.Type.MouseButtonPress: -# self.click_position = event.pos() -# return super().editorEvent(event, model, option, index) - -# def eventFilter(self, editor: Optional[QObject], event: Optional[QEvent]) -> bool: -# """By default, QPlainTextEdit doesn't handle enter or return""" - -# if editor is None or event is None: -# return False - -# if event.type() == QEvent.Type.Show: -# if self.click_position and isinstance(editor, QTextEdit): -# # Map click position to editor's local space -# local_click_position = editor.mapFromParent(self.click_position) - -# # Move cursor to the calculated position -# cursor = editor.cursorForPosition(local_click_position) -# editor.setTextCursor(cursor) - -# # Reset click position -# self.click_position = None - -# return False - -# elif event.type() == QEvent.Type.KeyPress: -# key_event = cast(QKeyEvent, event) -# key = key_event.key() -# if key == Qt.Key.Key_Return: -# if key_event.modifiers() == (Qt.KeyboardModifier.ControlModifier): -# self.commitData.emit(editor) -# self.closeEditor.emit(editor) -# return True - -# elif key == Qt.Key.Key_Escape: -# # Close editor if no changes have been made -# if hasattr(editor, "toPlainText"): -# data_modified = self.original_model_data.value() != editor.toPlainText() -# # if isinstance(editor, QPlainTextEdit): -# # data_modified = self.original_model_data.value() != editor.toPlainText() -# # elif isinstance(editor, QDoubleSpinBox): -# # data_modified = ( -# # self.original_model_data != int(editor.value()) * 1000 -# # ) -# if not data_modified: -# self.closeEditor.emit(editor) -# return True - -# discard_edits = QMessageBox.question( -# cast(QWidget, self.parent()), "Abandon edit", "Discard changes?" -# ) -# if discard_edits == QMessageBox.StandardButton.Yes: -# self.closeEditor.emit(editor) -# return True - -# return False - -# def sizeHint(self, option, index): -# self.initStyleOption(option, index) -# if self.current_editor: -# doc = self.current_editor.document() -# else: -# doc = QTextDocument() -# doc.setTextWidth(option.rect.width()) -# doc.setDefaultFont(option.font) -# doc.setDocumentMargin(0) -# doc.setHtml(option.text) -# return QSize(int(doc.idealWidth()), int(doc.size().height())) - -# def setEditorData(self, editor, index): -# proxy_model = index.model() -# edit_index = proxy_model.mapToSource(index) - -# self.original_model_data = self.source_model.data( -# edit_index, Qt.ItemDataRole.EditRole -# ) -# if index.column() == Col.INTRO.value: -# editor.setValue(self.original_model_data.value() / 1000) -# else: -# editor.setPlainText(self.original_model_data.value()) - -# def setModelData(self, editor, model, index): -# proxy_model = index.model() -# edit_index = proxy_model.mapToSource(index) - -# if isinstance(editor, QTextEdit): -# value = editor.toPlainText().strip() -# elif isinstance(editor, QDoubleSpinBox): -# value = editor.value() -# self.source_model.setData(edit_index, value, Qt.ItemDataRole.EditRole) - -# def updateEditorGeometry(self, editor, option, index): -# editor.setGeometry(option.rect) - - class PlaylistStyle(QProxyStyle): def drawPrimitive(self, element, option, painter, widget=None): """ @@ -490,7 +278,7 @@ class PlaylistTab(QTableView): # Set up widget self.source_model = PlaylistModel(playlist_id) self.proxy_model = PlaylistProxyModel(self.source_model) - self.setItemDelegate(TextDelegate(self, self.source_model)) + self.setItemDelegate(PlaylistDelegate(self, self.source_model)) self.setAlternatingRowColors(True) self.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel) self.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)