fix: usable side-by-side layout; make tab→folder a per-tab option
Side-by-side layout:
- Reduce pinned lists' min width (200→60) so two fit, widen the main
splitter's left section when split is active (steal from the video pane)
and restore on collapse. Fixed-height headers, list stretch, rebuild on
reparent — fixes the empty/misplaced panes.
Tab→folder per tab:
- Replace the global "Tab→folder" checkbox with a per-tab toggle in the tab
right-click menu ("Export to tab-named folder"). _tab_export_folder() now
reads the active tab's flag; persisted per tab.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3140,6 +3140,7 @@ class _PlaylistTabBar(QTabBar):
|
||||
"""
|
||||
tab_renamed = pyqtSignal(int, str)
|
||||
pin_toggle_requested = pyqtSignal(int)
|
||||
tab_folder_toggle_requested = pyqtSignal(int)
|
||||
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
idx = self.tabAt(event.pos())
|
||||
@@ -3161,10 +3162,15 @@ class _PlaylistTabBar(QTabBar):
|
||||
if hasattr(tw, "widget"):
|
||||
pw = tw.widget(idx)
|
||||
act_pin.setChecked(bool(getattr(pw, "_pinned", False)))
|
||||
act_tabfolder = menu.addAction("Export to tab-named folder")
|
||||
act_tabfolder.setCheckable(True)
|
||||
act_tabfolder.setChecked(bool(getattr(pw, "_tab_folder", False)))
|
||||
act_rename = menu.addAction("Rename…")
|
||||
chosen = menu.exec(event.globalPos())
|
||||
if chosen == act_pin:
|
||||
self.pin_toggle_requested.emit(idx)
|
||||
elif chosen == act_tabfolder:
|
||||
self.tab_folder_toggle_requested.emit(idx)
|
||||
elif chosen == act_rename:
|
||||
self._start_edit(idx)
|
||||
|
||||
@@ -3215,6 +3221,7 @@ class PlaylistWidget(QListWidget):
|
||||
self._separators_before: set[str] = set() # paths that show a separator row above
|
||||
self._missing: set[str] = set() # paths not present on disk
|
||||
self._pinned: bool = False # shown in the side-by-side view
|
||||
self._tab_folder: bool = False # append this tab's name to export folder
|
||||
self._label: str = "" # tab name (source of truth across views)
|
||||
self._visible: list[str | None] = [] # rows shown; None = separator row
|
||||
self._selected_path: str | None = None
|
||||
@@ -3726,6 +3733,8 @@ class MainWindow(QMainWindow):
|
||||
self._playlist_tabs.setDocumentMode(True)
|
||||
self._playlist_tabs.tabBar().tab_renamed.connect(self._on_tab_renamed)
|
||||
self._playlist_tabs.tabBar().pin_toggle_requested.connect(self._on_pin_toggle)
|
||||
self._playlist_tabs.tabBar().tab_folder_toggle_requested.connect(
|
||||
self._on_tab_folder_toggle)
|
||||
self._playlist_tabs.tabCloseRequested.connect(self._on_close_tab)
|
||||
self._playlist_tabs.currentChanged.connect(self._on_tab_changed)
|
||||
self._btn_add_tab = QPushButton("+")
|
||||
@@ -3833,16 +3842,6 @@ class MainWindow(QMainWindow):
|
||||
self._btn_folder.setFixedWidth(30)
|
||||
self._btn_folder.setToolTip("Browse for output folder")
|
||||
self._btn_folder.clicked.connect(self._pick_folder)
|
||||
self._chk_tab_folder = QCheckBox("Tab→folder")
|
||||
self._chk_tab_folder.setToolTip(
|
||||
"When on, append the active tab's name to the export folder\n"
|
||||
"(e.g. mp4 → mp4_BatchA) for files in that tab. Default 'List N'\n"
|
||||
"tabs are ignored.")
|
||||
self._chk_tab_folder.setChecked(
|
||||
self._settings.value("tab_in_folder", "false") == "true")
|
||||
self._chk_tab_folder.toggled.connect(
|
||||
lambda v: self._settings.setValue("tab_in_folder", "true" if v else "false"))
|
||||
self._chk_tab_folder.toggled.connect(self._on_tab_folder_toggled)
|
||||
self._spn_resize = QSpinBox()
|
||||
self._spn_resize.setRange(0, 4320)
|
||||
self._spn_resize.setSingleStep(64)
|
||||
@@ -4172,7 +4171,6 @@ class MainWindow(QMainWindow):
|
||||
path_row.addWidget(QLabel("Folder:"))
|
||||
path_row.addWidget(self._txt_folder, stretch=1)
|
||||
path_row.addWidget(self._btn_folder)
|
||||
path_row.addWidget(self._chk_tab_folder)
|
||||
|
||||
# Row 3 — video + encoding settings
|
||||
settings_row = QHBoxLayout()
|
||||
@@ -4277,6 +4275,7 @@ class MainWindow(QMainWindow):
|
||||
splitter.setCollapsible(0, False)
|
||||
splitter.setCollapsible(1, False)
|
||||
splitter.setCollapsible(2, True)
|
||||
self._main_splitter = splitter
|
||||
|
||||
self.setCentralWidget(splitter)
|
||||
self.setStatusBar(None)
|
||||
@@ -4476,9 +4475,11 @@ class MainWindow(QMainWindow):
|
||||
return label
|
||||
|
||||
def _tab_export_folder(self) -> str:
|
||||
"""The export base folder, with the active tab name appended when enabled."""
|
||||
"""The export base folder, with the active tab name appended when its
|
||||
per-tab 'Export to tab-named folder' option is enabled."""
|
||||
base = self._txt_folder.text()
|
||||
if getattr(self, "_chk_tab_folder", None) and self._chk_tab_folder.isChecked():
|
||||
pw = self._playlist
|
||||
if pw is not None and getattr(pw, "_tab_folder", False):
|
||||
name = self._active_tab_name()
|
||||
if name:
|
||||
base = base.rstrip(os.sep) + "_" + name
|
||||
@@ -4487,7 +4488,12 @@ class MainWindow(QMainWindow):
|
||||
def _export_base_name(self) -> str:
|
||||
return os.path.basename(self._tab_export_folder())
|
||||
|
||||
def _on_tab_folder_toggled(self, _checked: bool = False) -> None:
|
||||
def _on_tab_folder_toggle(self, idx: int) -> None:
|
||||
pw = self._playlist_tabs.widget(idx)
|
||||
if pw is None:
|
||||
return
|
||||
pw._tab_folder = not pw._tab_folder
|
||||
self._save_playlist_tabs()
|
||||
if self._file_path:
|
||||
self._refresh_markers()
|
||||
self._refresh_playlist_checks()
|
||||
@@ -4549,34 +4555,60 @@ class MainWindow(QMainWindow):
|
||||
if len(pinned) >= 2:
|
||||
for pw in self._pws:
|
||||
if not pw._pinned:
|
||||
pw.setMinimumWidth(0)
|
||||
self._playlist_tabs.addTab(pw, pw._label)
|
||||
splitter = QSplitter(Qt.Orientation.Horizontal)
|
||||
splitter.setChildrenCollapsible(False)
|
||||
for pw in pinned:
|
||||
panel = QWidget()
|
||||
pl = QVBoxLayout(panel)
|
||||
pl.setContentsMargins(0, 0, 0, 0)
|
||||
pl.setSpacing(0)
|
||||
hdr = QHBoxLayout()
|
||||
header = QWidget()
|
||||
hdr = QHBoxLayout(header)
|
||||
hdr.setContentsMargins(2, 1, 2, 1)
|
||||
lbl = QLabel(pw._label)
|
||||
lbl.setStyleSheet("font-weight: bold; padding: 2px 4px;")
|
||||
lbl.setStyleSheet("font-weight: bold;")
|
||||
btn = QPushButton("✕")
|
||||
btn.setFixedWidth(20)
|
||||
btn.setFixedSize(18, 18)
|
||||
btn.setToolTip("Remove from side-by-side")
|
||||
btn.clicked.connect(lambda _=False, w=pw: self._on_unpin(w))
|
||||
hdr.addWidget(lbl, 1)
|
||||
hdr.addWidget(btn)
|
||||
pl.addLayout(hdr)
|
||||
pl.addWidget(pw)
|
||||
header.setFixedHeight(22)
|
||||
pl.addWidget(header)
|
||||
pw.setMinimumWidth(60)
|
||||
pl.addWidget(pw, 1)
|
||||
pw._rebuild()
|
||||
splitter.addWidget(panel)
|
||||
splitter.setSizes([1000] * len(pinned))
|
||||
self._split_layout.addWidget(splitter)
|
||||
self._list_stack.setCurrentWidget(self._split_container)
|
||||
self._set_left_pane_width(max(420, len(pinned) * 240))
|
||||
else:
|
||||
for pw in self._pws:
|
||||
pw.setMinimumWidth(200)
|
||||
self._playlist_tabs.addTab(pw, pw._label)
|
||||
self._list_stack.setCurrentWidget(self._playlist_tabs)
|
||||
self._set_left_pane_width(220)
|
||||
finally:
|
||||
self._loading_tabs = prev
|
||||
|
||||
def _set_left_pane_width(self, want_left: int) -> None:
|
||||
"""Resize the main splitter's left section, stealing from the video pane."""
|
||||
sp = getattr(self, "_main_splitter", None)
|
||||
if sp is None:
|
||||
return
|
||||
sizes = sp.sizes()
|
||||
if len(sizes) != 3:
|
||||
return
|
||||
delta = want_left - sizes[0]
|
||||
if abs(delta) < 8:
|
||||
return
|
||||
sizes[0] = want_left
|
||||
sizes[1] = max(200, sizes[1] - delta)
|
||||
sp.setSizes(sizes)
|
||||
|
||||
def _on_pin_toggle(self, idx: int) -> None:
|
||||
pw = self._playlist_tabs.widget(idx)
|
||||
if pw is None:
|
||||
@@ -4640,6 +4672,7 @@ class MainWindow(QMainWindow):
|
||||
"files": list(pw._paths),
|
||||
"separators": sorted(pw._separators_before),
|
||||
"pinned": pw._pinned,
|
||||
"tab_folder": pw._tab_folder,
|
||||
} for pw in self._pws]
|
||||
cur = self._pws.index(self._active_pw) if self._active_pw in self._pws else 0
|
||||
data = {"tabs": tabs, "current": cur}
|
||||
@@ -4682,6 +4715,7 @@ class MainWindow(QMainWindow):
|
||||
files=list(t.get("files", [])),
|
||||
separators=t.get("separators", []), select=False)
|
||||
pw._pinned = bool(t.get("pinned"))
|
||||
pw._tab_folder = bool(t.get("tab_folder"))
|
||||
cur = min(max(0, data.get("current", 0)), len(self._pws) - 1)
|
||||
finally:
|
||||
self._loading_tabs = False
|
||||
|
||||
Reference in New Issue
Block a user