From 6f09c80aafa9d22bda3d45768a0be46fd2e075d7 Mon Sep 17 00:00:00 2001 From: Javanaut Date: Wed, 8 Jul 2026 09:47:07 +0200 Subject: [PATCH] ffcb --- src/ffx/pattern_details_screen.py | 74 ++++++++++++++++++++++- tests/unit/test_tag_table_screen_state.py | 47 ++++++++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/src/ffx/pattern_details_screen.py b/src/ffx/pattern_details_screen.py index 7d4f539..eb8948d 100644 --- a/src/ffx/pattern_details_screen.py +++ b/src/ffx/pattern_details_screen.py @@ -2,6 +2,7 @@ import click, re from typing import List from textual import events +from textual.css.query import NoMatches from textual.screen import Screen from textual.widgets import Header, Footer, Static, Button, Input, DataTable, TextArea from textual.containers import Grid @@ -153,6 +154,13 @@ class PatternDetailsScreen(Screen): def updateTracks(self): + selectedTrack = self.getSelectedTrackDescriptor() + selectedTrackIndex = ( + self._track_row_index(selectedTrack) + if selectedTrack is not None + else None + ) + selectedCursorColumn = getattr(self.tracksTable.cursor_coordinate, "column", 0) self.tracksTable.clear() self.__trackRowData = {} @@ -160,6 +168,7 @@ class PatternDetailsScreen(Screen): tracks = self.getCurrentTrackDescriptors() typeCounter = {} + restoredRowIndex = None td: TrackDescriptor for td in tracks: @@ -189,9 +198,72 @@ class PatternDetailsScreen(Screen): row_key = self.tracksTable.add_row(*map(str, row)) self.__trackRowData[row_key] = td + if ( + selectedTrack is not None + and self._descriptor_refs_same_track(td, selectedTrack) + ): + restoredRowIndex = len(self.__trackRowData) - 1 typeCounter[trackType] += 1 + if restoredRowIndex is None and selectedTrackIndex is not None and self.__trackRowData: + restoredRowIndex = min(selectedTrackIndex, len(self.__trackRowData) - 1) + + if restoredRowIndex is not None: + self.tracksTable.move_cursor( + row=restoredRowIndex, + column=selectedCursorColumn, + animate=False, + scroll=False, + ) + + self.updateTrackDropButtonState() + + def _track_row_index(self, trackDescriptor: TrackDescriptor | None): + if trackDescriptor is None: + return None + + for rowIndex, descriptor in enumerate(self.__trackRowData.values()): + if self._descriptor_refs_same_track(descriptor, trackDescriptor): + return rowIndex + return None + + def _descriptor_refs_same_track( + self, + descriptor: TrackDescriptor, + otherDescriptor: TrackDescriptor, + ) -> bool: + if descriptor.getId() != -1 and otherDescriptor.getId() != -1: + return descriptor.getId() == otherDescriptor.getId() + + return ( + descriptor.getPatternId() == otherDescriptor.getPatternId() + and descriptor.getIndex() == otherDescriptor.getIndex() + and descriptor.getSubIndex() == otherDescriptor.getSubIndex() + and descriptor.getType() == otherDescriptor.getType() + ) + + def updateTrackDropButtonState(self): + try: + button = self.query_one("#button_toggle_track_drop", Button) + except (NoMatches, AttributeError): + return + + selectedTrack = self.getSelectedTrackDescriptor() + button.disabled = selectedTrack is None + button.variant = ( + "warning" + if selectedTrack is not None and selectedTrack.isDropped() + else "default" + ) + + def on_data_table_row_highlighted( + self, + event: DataTable.RowHighlighted, + ) -> None: + if event.data_table.id == "tracks_table": + self.updateTrackDropButtonState() + def getCurrentTrackDescriptors(self) -> List[TrackDescriptor]: if self.__pattern is not None: @@ -482,7 +554,7 @@ class PatternDetailsScreen(Screen): yield Static(t("Streams")) yield Button(t("Add"), id="button_add_track") yield Button(t("Edit"), id="button_edit_track") - yield Button(t("Drop / Undrop"), id="button_toggle_track_drop") + yield Button(t("Drop"), id="button_toggle_track_drop") yield Static(" ") yield Button(t("Up"), id="button_track_up") diff --git a/tests/unit/test_tag_table_screen_state.py b/tests/unit/test_tag_table_screen_state.py index 961f746..6c90b67 100644 --- a/tests/unit/test_tag_table_screen_state.py +++ b/tests/unit/test_tag_table_screen_state.py @@ -76,6 +76,12 @@ class FakeTagTable: def select_row(self, row_key): self._selected_row_key = row_key + def move_cursor(self, *, row=None, column=None, animate=False, scroll=True): + if row is not None and self._row_order: + self._selected_row_key = self._row_order[int(row)] + if column is not None: + self.cursor_coordinate = (self.cursor_coordinate[0], int(column)) + def get_row_index(self, row_key): return self._row_order.index(row_key) @@ -110,6 +116,12 @@ class FakeValueWidget: self.disabled = False +class FakeButtonWidget: + def __init__(self): + self.disabled = False + self.variant = "default" + + class FakeInputWidget: def __init__(self, value): self.value = value @@ -379,6 +391,7 @@ class TagTableScreenStateTests(unittest.TestCase): def test_pattern_details_screen_toggles_draft_track_drop_state(self): first_track = make_track_descriptor(0, 0, TrackType.VIDEO) second_track = make_track_descriptor(1, 0, TrackType.SUBTITLE) + drop_button = FakeButtonWidget() screen = object.__new__(PatternDetailsScreen) screen.context = {"logger": get_ffx_logger()} @@ -386,6 +399,7 @@ class TagTableScreenStateTests(unittest.TestCase): screen._PatternDetailsScreen__draftTracks = [first_track, second_track] screen._PatternDetailsScreen__pattern = None screen._PatternDetailsScreen__trackRowData = {} + screen.query_one = lambda selector, _widget_type=None: drop_button screen.updateTracks() screen.tracksTable.select_row("row-1") @@ -395,6 +409,39 @@ class TagTableScreenStateTests(unittest.TestCase): self.assertFalse(first_track.isDropped()) self.assertTrue(second_track.isDropped()) self.assertEqual("Ja", list(screen.tracksTable.rows.values())[1][9]) + self.assertIs(second_track, screen.getSelectedTrackDescriptor()) + self.assertEqual("warning", drop_button.variant) + + def test_pattern_details_screen_updates_drop_button_for_selected_track(self): + first_track = make_track_descriptor(0, 0, TrackType.VIDEO) + second_track = make_track_descriptor( + 1, + 0, + TrackType.SUBTITLE, + dropped=True, + ) + drop_button = FakeButtonWidget() + + screen = object.__new__(PatternDetailsScreen) + screen.tracksTable = FakeTagTable() + screen._PatternDetailsScreen__draftTracks = [first_track, second_track] + screen._PatternDetailsScreen__pattern = None + screen._PatternDetailsScreen__trackRowData = {} + screen.query_one = lambda selector, _widget_type=None: drop_button + + screen.updateTracks() + + self.assertEqual("default", drop_button.variant) + + screen.tracksTable.select_row("row-1") + screen.updateTrackDropButtonState() + + self.assertEqual("warning", drop_button.variant) + + screen.tracksTable.select_row("row-0") + screen.updateTrackDropButtonState() + + self.assertEqual("default", drop_button.variant) def test_pattern_details_screen_toggles_persisted_track_drop_state(self): class _FakePattern: