feat: double-click a merge clip to set its join crossfade/curve override + row labels

This commit is contained in:
2026-07-02 17:50:40 +02:00
parent 65200e482e
commit 68368b4ff6
2 changed files with 80 additions and 3 deletions
+59 -3
View File
@@ -4684,6 +4684,7 @@ class MainWindow(QMainWindow):
self._merge_list.setSelectionMode(
QAbstractItemView.SelectionMode.SingleSelection)
self._merge_list.setToolTip("Clips to merge, top to bottom")
self._merge_list.itemDoubleClicked.connect(self._on_merge_item_double_clicked)
self._merge_temps: set[str] = set()
self._spn_crossfade = QDoubleSpinBox()
self._spn_crossfade.setRange(0.0, 10.0)
@@ -4695,9 +4696,7 @@ class MainWindow(QMainWindow):
self._spn_crossfade.valueChanged.connect(
lambda v: self._settings.setValue("audio_crossfade", v))
self._cmb_curve = QComboBox()
for label, name in (("Triangular", "tri"), ("Exponential", "exp"),
("Logarithmic", "log"), ("Quarter sine", "qsin"),
("Half sine", "hsin")):
for label, name in self._MERGE_CURVES:
self._cmb_curve.addItem(label, name)
self._cmb_curve.setToolTip("Crossfade curve at each join")
self._btn_merge_add_sel = QPushButton(" Selection")
@@ -5181,6 +5180,9 @@ class MainWindow(QMainWindow):
APP_VERSION = "1.5"
_SPLIT_HEADER_H = 22 # deck split-column header height (keep both deck spots in sync)
_WAVE_MIN_VIEW = 3.0 # minimum waveform view window (seconds)
_MERGE_CURVES = (("Triangular", "tri"), ("Exponential", "exp"),
("Logarithmic", "log"), ("Quarter sine", "qsin"),
("Half sine", "hsin"))
CHANGELOG: list[tuple[str, list[str]]] = [
("1.5", [
"<b>Interactive waveform</b> — the Audio tab's waveform is now a "
@@ -6760,9 +6762,29 @@ class MainWindow(QMainWindow):
item.setData(Qt.ItemDataRole.UserRole + 2, None) # crossfade override (None = global)
item.setData(Qt.ItemDataRole.UserRole + 3, None) # curve override (None = global)
self._merge_list.addItem(item)
self._refresh_merge_labels()
finally:
QApplication.restoreOverrideCursor()
def _merge_item_label(self, item, index: int, count: int) -> str:
name = os.path.basename(item.data(Qt.ItemDataRole.UserRole))
dur = item.data(Qt.ItemDataRole.UserRole + 1)
base = f"{name} ({dur:.2f}s)" if dur is not None else name
if index < count - 1: # has an outgoing join
xo = item.data(Qt.ItemDataRole.UserRole + 2)
co = item.data(Qt.ItemDataRole.UserRole + 3)
if xo is not None or co is not None:
xf = xo if xo is not None else self._spn_crossfade.value()
cv = co if co is not None else self._cmb_curve.currentData()
base += f"{xf:.2f}s {cv}" # ⤲ join marker
return base
def _refresh_merge_labels(self) -> None:
n = self._merge_list.count()
for i in range(n):
it = self._merge_list.item(i)
it.setText(self._merge_item_label(it, i, n))
def _on_merge_add_file(self) -> None:
default_dir = (self._settings.value("audio_extract_dir", "")
or os.path.dirname(self._file_path or ""))
@@ -6811,11 +6833,45 @@ class MainWindow(QMainWindow):
item = self._merge_list.takeItem(row)
self._merge_list.insertItem(new, item)
self._merge_list.setCurrentRow(new)
self._refresh_merge_labels()
def _merge_remove_selected(self) -> None:
row = self._merge_list.currentRow()
if row >= 0:
self._merge_list.takeItem(row)
self._refresh_merge_labels()
def _apply_merge_override(self, item, crossfade: float, curve: str) -> None:
item.setData(Qt.ItemDataRole.UserRole + 2, float(crossfade))
item.setData(Qt.ItemDataRole.UserRole + 3, curve)
self._refresh_merge_labels()
def _on_merge_item_double_clicked(self, item) -> None:
row = self._merge_list.row(item)
if row >= self._merge_list.count() - 1:
self._show_status("The last clip has no outgoing join to crossfade", 4000)
return
dlg = QDialog(self)
dlg.setWindowTitle("Join crossfade")
form = QFormLayout(dlg)
spn = QDoubleSpinBox()
spn.setRange(0.0, 10.0); spn.setDecimals(2); spn.setSingleStep(0.1); spn.setSuffix(" s")
xo = item.data(Qt.ItemDataRole.UserRole + 2)
spn.setValue(float(xo) if xo is not None else self._spn_crossfade.value())
cmb = QComboBox()
for label, name in self._MERGE_CURVES:
cmb.addItem(label, name)
co = item.data(Qt.ItemDataRole.UserRole + 3)
co = co if co is not None else self._cmb_curve.currentData()
idx = cmb.findData(co); cmb.setCurrentIndex(idx if idx >= 0 else 0)
form.addRow("Crossfade:", spn)
form.addRow("Curve:", cmb)
bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
| QDialogButtonBox.StandardButton.Cancel)
bb.accepted.connect(dlg.accept); bb.rejected.connect(dlg.reject)
form.addRow(bb)
if dlg.exec():
self._apply_merge_override(item, spn.value(), cmb.currentData())
def _merge_paths(self) -> list[str]:
return [self._merge_list.item(i).data(Qt.ItemDataRole.UserRole)