From a03449a32bcf2a6fae08e2fcf22aaf8aeb26c6b2 Mon Sep 17 00:00:00 2001 From: Maveno Date: Sun, 6 Oct 2024 12:21:27 +0200 Subject: [PATCH] add/edit tag to ui --- bin/ffx/tag_delete_screen.py | 142 +++++++++++++++++++++++++ bin/ffx/tag_details_screen.py | 125 ++++++++++++++++++++++ bin/ffx/track_details_screen.py | 180 ++++++++++++++++++++++++++++---- 3 files changed, 427 insertions(+), 20 deletions(-) create mode 100644 bin/ffx/tag_delete_screen.py create mode 100644 bin/ffx/tag_details_screen.py diff --git a/bin/ffx/tag_delete_screen.py b/bin/ffx/tag_delete_screen.py new file mode 100644 index 0000000..da68e78 --- /dev/null +++ b/bin/ffx/tag_delete_screen.py @@ -0,0 +1,142 @@ +import click + +from textual import events +from textual.app import App, ComposeResult +from textual.screen import Screen +from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input +from textual.containers import Grid, Horizontal + +from ffx.model.pattern import Pattern +from ffx.track_descriptor import TrackDescriptor + +# from .show_controller import ShowController +# from .pattern_controller import PatternController +from .track_controller import TrackController + +from .track_type import TrackType + +# Screen[dict[int, str, int]] +class TrackDeleteScreen(Screen): + + CSS = """ + + Grid { + grid-size: 4 9; + grid-rows: 2 2 2 2 2 2 2 2 2; + grid-columns: 30 30 30 30; + height: 100%; + width: 100%; + padding: 1; + } + + Input { + border: none; + } + Button { + border: none; + } + #toplabel { + height: 1; + } + + .two { + column-span: 2; + } + .three { + column-span: 3; + } + .four { + column-span: 4; + } + + .box { + height: 100%; + border: solid green; + } + """ + + def __init__(self, trackDescriptor : TrackDescriptor): + super().__init__() + + self.context = self.app.getContext() + self.Session = self.context['database']['session'] # convenience + + if type(trackDescriptor) is not TrackDescriptor: + raise click.ClickException('TrackDeleteScreen.init(): trackDescriptor is required to be of type TrackDescriptor') + + self.__tc = TrackController(context = self.context) + + self.__trackDescriptor = trackDescriptor + + + def on_mount(self): + + self.query_one("#subindexlabel", Static).update(str(self.__trackDescriptor.getSubIndex())) + self.query_one("#patternlabel", Static).update(str(self.__trackDescriptor.getPatternId())) + self.query_one("#languagelabel", Static).update(str(self.__trackDescriptor.getLanguage().label())) + self.query_one("#titlelabel", Static).update(str(str(self.__trackDescriptor.getTitle()))) + + + def compose(self): + + yield Header() + + with Grid(): + + #1 + yield Static(f"Are you sure to delete the following {self.__trackDescriptor.getType().label()} track?", id="toplabel", classes="four") + + #2 + yield Static("sub index") + yield Static(" ", id="subindexlabel", classes="three") + + #3 + yield Static("from pattern") + yield Static(" ", id="patternlabel", classes="three") + + #4 + yield Static(" ", classes="four") + + #5 + yield Static("Language") + yield Static(" ", id="languagelabel", classes="three") + + #6 + yield Static("Title") + yield Static(" ", id="titlelabel", classes="three") + + #7 + yield Static(" ", classes="four") + + #8 + yield Static(" ", classes="four") + + #9 + yield Button("Delete", id="delete_button") + yield Button("Cancel", id="cancel_button") + + yield Footer() + + + # Event handler for button press + def on_button_pressed(self, event: Button.Pressed) -> None: + + if event.button.id == "delete_button": + + track = self.__tc.findTrack(self.__trackDescriptor.getPatternId(), self.__trackDescriptor.getType(), self.__trackDescriptor.getSubIndex()) + + if track is None: + raise click.ClickException(f"Track is none: patternId={self.__trackDescriptor.getPatternId()} type={self.__trackDescriptor.getType()} subIndex={self.__trackDescriptor.getSubIndex()}") + + if track is not None: + + if self.__tc.deleteTrack(track.getId()): + self.dismiss(self.__trackDescriptor) + + else: + #TODO: Meldung + self.app.pop_screen() + + if event.button.id == "cancel_button": + self.app.pop_screen() + diff --git a/bin/ffx/tag_details_screen.py b/bin/ffx/tag_details_screen.py new file mode 100644 index 0000000..c85500f --- /dev/null +++ b/bin/ffx/tag_details_screen.py @@ -0,0 +1,125 @@ +import click, time + +from textual import events +from textual.app import App, ComposeResult +from textual.screen import Screen +from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input, Checkbox, SelectionList, Select +from textual.containers import Grid, Horizontal + +# Screen[dict[int, str, int]] +class TagDetailsScreen(Screen): + + CSS = """ + + Grid { + grid-size: 5 20; + grid-rows: 2 2 2 2 2 3 2 2 2 2 2 6 2 2 6 2 2 2 2 6; + grid-columns: 25 25 25 25 225; + height: 100%; + width: 100%; + padding: 1; + } + + Input { + border: none; + } + Button { + border: none; + } + SelectionList { + border: none; + min-height: 6; + } + Select { + border: none; + } + DataTable { + min-height: 6; + } + + #toplabel { + height: 1; + } + + .two { + column-span: 2; + } + .three { + column-span: 3; + } + + .four { + column-span: 4; + } + .five { + column-span: 5; + } + + .box { + height: 100%; + border: solid green; + } + """ + + def __init__(self, key=None, value=None): + super().__init__() + self.__key = key + self.__value = value + + + + def on_mount(self): + + if self.__key is not None: + self.query_one("#key_input", Input).value = str(self.__key) + + if self.__value is not None: + self.query_one("#value_input", Input).value = str(self.__value) + + + def compose(self): + + yield Header() + + with Grid(): + + # 8 + yield Static("Key") + yield Input(id="key_input", classes="four") + + yield Static("Value") + yield Input(id="value_input", classes="four") + + # 17 + yield Static(" ", classes="five") + + # 18 + yield Button("Save", id="save_button") + yield Button("Cancel", id="cancel_button") + + # 19 + yield Static(" ", classes="five") + + # 20 + yield Static(" ", classes="five", id="messagestatic") + + yield Footer(id="footer") + + + def getTagFromInput(self): + + tagKey = self.query_one("#key_input", Input).value + tagValue = self.query_one("#value_input", Input).value + + return (tagKey, tagValue) + + + # Event handler for button press + def on_button_pressed(self, event: Button.Pressed) -> None: + + # Check if the button pressed is the one we are interested in + if event.button.id == "save_button": + self.dismiss(self.getTagFromInput()) + + if event.button.id == "cancel_button": + self.app.pop_screen() diff --git a/bin/ffx/track_details_screen.py b/bin/ffx/track_details_screen.py index 06c0b7e..b7c4622 100644 --- a/bin/ffx/track_details_screen.py +++ b/bin/ffx/track_details_screen.py @@ -5,7 +5,7 @@ from textual.app import App, ComposeResult from textual.screen import Screen from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input, Checkbox, SelectionList, Select from textual.containers import Grid, Horizontal - +from textual.coordinate import Coordinate from ffx.model.show import Show from ffx.model.pattern import Pattern @@ -22,6 +22,76 @@ from .audio_layout import AudioLayout from .track_descriptor import TrackDescriptor +from .tag_details_screen import TagDetailsScreen + +from textual.widgets._data_table import CellDoesNotExist + + +class EditableDataTable(DataTable): + """Custom DataTable that allows editing cells upon mouse click.""" + + def on_click(self, event: events.Click) -> None: + """Handle mouse clicks on the table.""" + + # raise click.ClickException(f"event={event}") + + #event.x und event.y sind koordinaten im Datatable + + #clicked_cell = self.coordinate_to_cell_key(self.cursor_coordinate) + #raise click.ClickException(f"x={event.x} y={event.y} clicked_cell={clicked_cell}") + + + #raise click.ClickException(f"cuco={self.cursor_coordinate}") + + #clickCoordinates = Coordinate(row = event.y, column=event.x) + + #clicked_cell = self.get_cell_at(event.x, event.y) + #row_key, col_key = self.coordinate_to_cell_key(self.cursor_coordinate) + + #raise click.ClickException(f"x={event.x} y={event.y} row={row_key} col={col_key}") + + + #if clicked_cell: + # if row_key is not None and col_key is not None: + #row, column = clicked_cell + # Get the current cell value and activate editing. + #current_value = self.get_cell(row_key, col_key) + current_value = self.get_cell_at(self.cursor_coordinate) + + region = self._get_cell_region(self.cursor_coordinate) + + raise click.ClickException(f"region={region}") + + #current_value = self.get_cell(row_key, col_key) + #self.activate_cell_edit(row_key, col_key, current_value) + + raise click.ClickException(f"val={current_value}") + + def activate_cell_edit(self, row: int, column: int, value: str) -> None: +# """Method to activate cell editing.""" +# # Create a new input box with the value of the cell to be edited. + input_box = Input(value=value, classes="cell-editor") + input_box.styles.width = len(value) + 2 # Adjust the width of the input box + input_box.styles.padding = (0, 1) +# +# # Position the input box over the cell to be edited. + input_box.styles.position = "absolute" + input_box.styles.left = self.get_cell_position(row, column)[0] + input_box.styles.top = self.get_cell_position(row, column)[1] + +# # Add the input box to the parent widget for editing. +# self.app.mount(input_box) +# input_box.focus() +# +# # Listen to when the user completes editing. +# input_box.on_blur = lambda event: self.save_cell_value(row, column, input_box.value) +# +# def save_cell_value(self, row: int, column: int, value: str) -> None: +# """Save the new value to the table and remove the input box.""" +# self.update_cell(row, column, value) +# self.app.query_one(".cell-editor").remove() # Remove the input box after saving. + + # Screen[dict[int, str, int]] class TrackDetailsScreen(Screen): @@ -87,23 +157,6 @@ class TrackDetailsScreen(Screen): self.__tc = TrackController(context = self.context) self.__pc = PatternController(context = self.context) - - INDEX_KEY = 'index' - SUB_INDEX_KEY = 'sub_index' - PATTERN_ID_KEY = 'pattern_id' - - TRACK_TYPE_KEY = 'track_type' - DISPOSITION_SET_KEY = 'disposition_set' - TAGS_KEY = 'tags' - AUDIO_LAYOUT_KEY = 'audio_layout' - -# if trackDescriptor is None: -# self.__trackDescriptor = TrackDescriptor(index=, -# sub_index= -# pattern_id=patternId, -# track_type=trackType) -# else: - self.__isNew = trackDescriptor is None if self.__isNew: self.__trackType = trackType @@ -144,7 +197,7 @@ class TrackDetailsScreen(Screen): def compose(self): - self.trackTagsTable = DataTable(classes="five") + self.trackTagsTable = EditableDataTable(classes="five") # Define the columns with headers self.column_key_track_tag_key = self.trackTagsTable.add_column("Key", width=10) @@ -193,8 +246,9 @@ class TrackDetailsScreen(Screen): # 11 yield Static("Stream tags") - yield Static(" ", classes="two") + yield Static(" ") yield Button("Add", id="button_add_stream_tag") + yield Button("Edit", id="button_edit_stream_tag") yield Button("Delete", id="button_delete_stream_tag") # 12 yield self.trackTagsTable @@ -259,6 +313,31 @@ class TrackDetailsScreen(Screen): return TrackDescriptor(**kwargs) + + def getSelectedTag(self): + + try: + + # Fetch the currently selected row when 'Enter' is pressed + #selected_row_index = self.table.cursor_row + row_key, col_key = self.trackTagsTable.coordinate_to_cell_key(self.trackTagsTable.cursor_coordinate) + + if row_key is not None: + selected_tag_data = self.trackTagsTable.get_row(row_key) + + tagKey = str(selected_tag_data[0]) + tagValue = str(selected_tag_data[1]) + + return tagKey, tagValue + + else: + return None + + except CellDoesNotExist: + return None + + + # Event handler for button press def on_button_pressed(self, event: Button.Pressed) -> None: @@ -308,3 +387,64 @@ class TrackDetailsScreen(Screen): if event.button.id == "cancel_button": self.app.pop_screen() + + + + if event.button.id == "button_add_stream_tag": + self.app.push_screen(TagDetailsScreen(), self.handle_add_tag) + + #row = ('','') + #self.trackTagsTable.add_row(*map(str, row)) + + if event.button.id == "button_edit_stream_tag": + tagKey, tagValue = self.getSelectedTag() + self.app.push_screen(TagDetailsScreen(key=tagKey, value=tagValue), self.handle_add_tag) + + if event.button.id == "button_delete_stream_tag": + pass + + + + def handle_add_tag(self, tag): + + row = (tag[0], tag[1]) + self.trackTagsTable.add_row(*map(str, row)) + + + def handle_edit_tag(self, trackDescriptor : TrackDescriptor): + pass +# try: +# if trackDescriptor.getType() == TrackType.AUDIO: +# +# row_key, col_key = self.audioStreamsTable.coordinate_to_cell_key(self.audioStreamsTable.cursor_coordinate) +# +# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_language, trackDescriptor.getLanguage().label()) +# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_title, trackDescriptor.getTitle()) +# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_default, 'Yes' if TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() else 'No') +# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_forced, 'Yes' if TrackDisposition.FORCED in trackDescriptor.getDispositionSet() else 'No') +# +# if trackDescriptor.getType() == TrackType.SUBTITLE: +# +# row_key, col_key = self.subtitleStreamsTable.coordinate_to_cell_key(self.subtitleStreamsTable.cursor_coordinate) +# +# self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_language, trackDescriptor.getLanguage().label()) +# self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_title, trackDescriptor.getTitle()) +# self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_default, 'Yes' if TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() else 'No') +# self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_forced, 'Yes' if TrackDisposition.FORCED in trackDescriptor.getDispositionSet() else 'No') +# +# except CellDoesNotExist: +# pass + + + def handle_delete_tag(self, trackDescriptor : TrackDescriptor): + pass +# try: +# if trackDescriptor.getType() == TrackType.AUDIO: +# self.updateAudioTracks() +# +# if trackDescriptor.getType() == TrackType.SUBTITLE: +# self.updateSubtitleTracks() +# +# except CellDoesNotExist: +# pass +