import { Button, VerticalBox , HorizontalBox, TabWidget, ListView, StandardListView, StandardTableView, CheckBox, ScrollView} from "std-widgets.slint"; import {TypeOfOpenedItem} from "common.slint"; import {ColorPalette} from "color_palette.slint"; import {MainListModel} from "common.slint"; import {Callabler} from "callabler.slint"; import {GuiState} from "gui_state.slint"; export component SelectableTableView inherits Rectangle { callback item_opened(string); in property <[string]> columns; in-out property <[MainListModel]> values: [ {checked: false, selected_row: false, header_row: true, filled_header_row: false, val_str: ["kropkarz", "/Xd1", "24.10.2023"], val_int: []} , {checked: false, selected_row: false, header_row: false, filled_header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , {checked: false, selected_row: false, header_row: false, filled_header_row: false, val_str: ["witasphere", "/Xd1/Imagerren2", "25.11.1991"], val_int: []} , {checked: true, selected_row: false, header_row: false, filled_header_row: false, val_str: ["lokkaler", "/Xd1/Vide2", "01.23.1911"], val_int: []} ]; in-out property <[length]> column_sizes: [30px, 80px, 150px, 160px]; private property column_number: column_sizes.length + 1; // This idx, starts from zero, but since first is always a checkbox, and is not in model.val values, remove 1 from idx in-out property parentPathIdx; in-out property fileNameIdx; in-out property selected_item: -1; out property list_view_width: max(self.width - 20px, column_sizes[0] + column_sizes[1] + column_sizes[2] + column_sizes[3] + column_sizes[4] + column_sizes[5] + column_sizes[6] + column_sizes[7] + column_sizes[8] + column_sizes[9] + column_sizes[10] + column_sizes[11]); VerticalBox { padding: 0px; ScrollView { height: 30px; viewport-x <=> list_view.viewport-x; vertical-stretch: 0; HorizontalLayout { spacing: 5px; for title [idx] in root.columns: HorizontalLayout { width: root.column_sizes[idx]; Text { overflow: elide; text: title; } Rectangle { width: 1px; background: gray; TouchArea { width: 8px; x: (parent.width - self.width) / 2; property cached; pointer-event(event) => { if (event.button == PointerEventButton.left && event.kind == PointerEventKind.down) { self.cached = root.column_sizes[idx]; } } moved => { if (self.pressed) { root.column_sizes[idx] += (self.mouse-x - self.pressed-x); if (root.column_sizes[idx] < 20px) { root.column_sizes[idx] = 20px; } } } mouse-cursor: ew-resize; } } } } } list_view := ListView { padding: 0px; min-width: 100px; for r [idx] in root.values: Rectangle { width: list_view_width; border-radius: 5px; height: 20px; background: ColorPalette.get_listview_color_with_header(r.selected_row, touch-area.has-hover, r.header_row); touch_area := TouchArea { function clicked_manual() { // We don't allow to select header row // unless it contains data, which is true only for for modes with reference folders if (!r.header_row || r.val_str.length > 0) { if (root.selected_item == -1) { r.selected_row = !r.selected_row; root.selected_item = idx; } else { if (!r.selected_row && root.selected_item != idx) { r.selected_row = !r.selected_row; root.values[root.selected_item].selected_row = false; root.selected_item = idx; } } if (root.selected_item != -1) { showPreview(); } else { GuiState.preview_visible = false; } } } double-clicked => { if (r.header_row && !r.filled_header_row) { return; } openSelectedItem(); } pointer-event(event) => { // TODO this should be clicked by double-click - https://github.com/slint-ui/slint/issues/4235 if (event.button == PointerEventButton.right && event.kind == PointerEventKind.up) { if (r.header_row && !r.filled_header_row) { return; } Callabler.item_opened(r.val_str[root.parentPathIdx - 1]) } else if (event.button == PointerEventButton.left && event.kind == PointerEventKind.up) { clicked_manual(); } //else if (event.button == PointerEventButton.middle && event.kind == PointerEventKind.up) { // Callabler.item_opened(r.val_str[root.parentPathIdx - 1] + "/" + r.val_str[root.fileNameIdx - 1]) //} } } HorizontalLayout { CheckBox { visible: !r.header_row; checked: r.checked && !r.header_row; width: root.column_sizes[0]; toggled => { r.checked = self.checked; } } HorizontalLayout { spacing: 5px; for f [idx] in r.val_str: Text { width: root.column_sizes[idx + 1]; text: f; font-size: 12px; vertical-alignment: center; overflow: elide; } } } } } } public function deselect_selected_item() { if (root.selected_item != -1) { root.values[root.selected_item].selected_row = false; root.selected_item = -1; } } function showPreview() { Callabler.load_image_preview(root.values[root.selected_item].val_str[root.parentPathIdx - 1] + "/" + root.values[root.selected_item].val_str[root.fileNameIdx - 1]); } function openSelectedItem() { Callabler.item_opened(root.values[root.selected_item].val_str[root.parentPathIdx - 1] + "/" + root.values[root.selected_item].val_str[root.fileNameIdx - 1]); } // TODO this should work with multiple selection and shift and control key - problably logic will need to be set in global state public function released_key(event: KeyEvent) { if (event.text == " ") { if (root.selected_item != -1 && !root.values[root.selected_item].header_row) { root.values[root.selected_item].checked = !root.values[root.selected_item].checked; } } else if (event.text == "\n" ) { if (root.selected_item != -1) { openSelectedItem(); } } else if (event.text == Key.DownArrow) { if (root.selected_item != -1) { if (root.values.length - 1 == root.selected_item) { // Last element, so unselect it root.values[root.selected_item].selected_row = false; root.selected_item = -1; } else { // Select next item, if next item is header row, then select second // This should be safe, because header row should never be last item root.values[root.selected_item].selected_row = false; if (root.values[root.selected_item + 1].header_row) { root.selected_item += 2; } else { root.selected_item += 1; } root.values[root.selected_item].selected_row = true; showPreview(); } } else { // Select last item if nothing is selected if (root.values.length > 0) { if (root.values[0].header_row) { root.selected_item = 1; } else { root.selected_item = 0; } root.values[root.selected_item].selected_row = true; showPreview(); } } } else if (event.text == Key.UpArrow) { if (root.selected_item != -1) { if (root.selected_item == 0) { // First element, so unselect it root.values[root.selected_item].selected_row = false; root.selected_item = -1; } else { root.values[root.selected_item].selected_row = false; // Select previous item, if previous item is header row, then select second previous item // This is safe, because if there is non header row upper, then can be easily selected, // but otherwise is done -2 which for 1 (smallest possible item to set with header row) gives -1, so gives // this non selected row if (root.values[root.selected_item - 1].header_row) { root.selected_item -= 2; } else { root.selected_item -= 1; } if (root.selected_item != -1) { root.values[root.selected_item].selected_row = true; showPreview(); } } } else { // Select last item if nothing is selected if (root.values.length > 0) { root.selected_item = root.values.length - 1; root.values[root.selected_item].selected_row = true; showPreview(); } } } } }