// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial import { LineEditInner } from "../common/common.slint"; import { Palette, ScrollView } from "std-widgets-impl.slint"; component TableViewColumn inherits Rectangle { callback clicked <=> touch-area.clicked; callback adjust_size(length); in property sort-order: SortOrder.unsorted; touch-area := TouchArea { width: parent.width - 11px; } HorizontalLayout { padding-left: 16px; padding-right: 16px; spacing: 2px; HorizontalLayout { @children } Path { visible: root.sort-order != SortOrder.unsorted; width: 12px; height: 12px; y: (parent.height - self.height) / 2; commands: root.sort-order == SortOrder.ascending ? "M6 10.5C6.27614 10.5 6.5 10.2761 6.5 10V3.20711L9.14645 5.85355C9.34171 6.04882 9.65829 6.04882 9.85355 5.85355C10.0488 5.65829 10.0488 5.34171 9.85355 5.14645L6.35355 1.64645C6.15829 1.45118 5.84171 1.45118 5.64645 1.64645L2.14645 5.14645C1.95118 5.34171 1.95118 5.65829 2.14645 5.85355C2.34171 6.04882 2.65829 6.04882 2.85355 5.85355L5.5 3.20711V10C5.5 10.2761 5.72386 10.5 6 10.5Z" : "M6 1.5C6.27614 1.5 6.5 1.72386 6.5 2V8.79289L9.14645 6.14645C9.34171 5.95118 9.65829 5.95118 9.85355 6.14645C10.0488 6.34171 10.0488 6.65829 9.85355 6.85355L6.35355 10.3536C6.15829 10.5488 5.84171 10.5488 5.64645 10.3536L2.14645 6.85355C1.95118 6.65829 1.95118 6.34171 2.14645 6.14645C2.34171 5.95118 2.65829 5.95118 2.85355 6.14645L5.5 8.79289V2C5.5 1.72386 5.72386 1.5 6 1.5Z"; fill: Palette.neutralDark; } } // border Rectangle { y: parent.height - self.height; width: 100%; height: 1px; background: Palette.neutralLight; } Rectangle { width: 1px; x: parent.width - 1px; background: movable-ta.has-hover ? Palette.neutralTertiary : transparent; animate background { duration: 250ms; } movable-ta := TouchArea { width: 10px; moved => { if (self.pressed) { adjust_size(self.mouse-x - self.pressed-x); } } mouse-cursor: ew-resize; } } states [ pressed when touch-area.pressed : { background: Palette.neutralLight; } hover when touch-area.has-hover : { background: Palette.neutralLighter; } ] } component TableViewCell inherits Rectangle { clip: true; HorizontalLayout { padding-left: 16px; padding-right: 16px; @children } // border Rectangle { y: parent.height - self.height; width: 100%; height: 1px; background: Palette.neutralLight; } } component TableViewRow inherits Rectangle { callback clicked <=> touch-area.clicked; in property selected; min-height: 42px; touch-area := TouchArea {} HorizontalLayout { @children } states [ selected when root.selected : { background: Palette.neutralLighter; } hover when touch-area.has-hover : { background: Palette.neutralLighter; } ] } export component StandardTableView { private property item-height: scroll-view.viewport-height / rows.length; private property current-item-y: scroll-view.viewport-y + current-row * item-height; callback sort-ascending(int); callback sort-descending(int); private property min-header-height: 42px; out property current-sort-column: -1; in-out property <[TableColumn]> columns; in property <[[StandardListViewItem]]> rows; in-out property current-row: -1; min-width: 400px; min-height: 200px; horizontal-stretch: 1; vertical-stretch: 1; public function set-current-row(index: int) { if(index < 0 || index >= rows.length) { return; } current-row = index; if(current-item-y < 0) { scroll-view.viewport-y += 0 - current-item-y; } if(current-item-y + item-height > scroll-view.visible-height) { scroll-view.viewport-y -= current-item-y + item-height - scroll-view.visible-height; } } function sort(index: int) { if (root.current-sort-column != index) { root.columns[root.current-sort-column].sort-order = SortOrder.unsorted; } if(root.columns[index].sort-order == SortOrder.ascending) { root.columns[index].sort-order = SortOrder.descending; root.sort-descending(index); } else { root.columns[index].sort-order = SortOrder.ascending; root.sort-ascending(index); } root.current-sort-column = index; } VerticalLayout { Rectangle { clip: true; vertical-stretch: 0; min-height: header-layout.min-height; header-layout := HorizontalLayout { width: max(self.preferred-width, parent.width); x: scroll-view.viewport-x; padding-right: 20px; min-height: root.min-header-height; for column[index] in root.columns : TableViewColumn { sort-order: column.sort-order; horizontal-stretch: column.horizontal-stretch; min-width: max(column.min-width, column.width); preferred-width: self.min-width; max-width: (index < columns.length && column.width >= 1px) ? max(column.min-width, column.width) : 100000px; Text { vertical-alignment: center; text: column.title; font-weight: 900; overflow: elide; } clicked => { root.sort(index); } adjust-size(diff) => { column.width = max(1px, self.width + diff); } } } } scroll-view := ScrollView { vertical-stretch: 1; VerticalLayout { alignment: start; for row[idx] in root.rows : TableViewRow { selected: idx == root.current-row; for cell[index] in row : TableViewCell { private property has_inner_focus; horizontal-stretch: root.columns[index].horizontal-stretch; min-width: max(columns[index].min-width, columns[index].width); preferred-width: self.min-width; max-width: (index < columns.length && columns[index].width >= 1px) ? max(columns[index].min-width, columns[index].width) : 100000px; Rectangle { Text { width: 100%; height: 100%; overflow: elide; vertical-alignment: center; text: cell.text; } } } clicked => { root.set-current-row(idx); } } } } } FocusScope { key-pressed(event) => { if (event.text == Key.UpArrow) { root.set-current-row(root.current-row - 1); return accept; } else if (event.text == Key.DownArrow) { root.set-current-row(root.current-row + 1); return accept; } reject } } }