mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-27 12:29:41 +00:00

* Optimize StandardTableView in Fluent and Material styles By using a ListView instead of a ScrollView + VerticalLayout, we trigger the optimization where only visible children are being instantiated. For the Fluent style, this was making the rows more compact, so I've added additional padding to the TableViewCell to compensate. I don't know where the padding used to come from. I didn't touch Cupertino style since it's currently work-in-progress. This optimization can be done there as well. For the Native style, this optimization isn't as straight-forward because it uses a NativeScrollView + Flickable directly rather than a the ScrollView. * Optimize StandardTableView for Native style Using the ListView makes it possible to only instantiate the visible items, which makes the StandardTableView a lot faster for larger models (already very noticeably in the gallery example). When using a ListView, StandardTableView lost access to the NativeScrollView.native-padding-top/left properties, which are now exposed through the ScrollView item. It could also no longer position the Flickable below the header (or in doing so, would take the scrollbar along with it), so a property was added to ScrollView for that purpose as well. * Introduced InternalScrollView This avoids exposing additional properties on the ScrollView for just the native style. * Fix missing default values for Native style ScrollView Added minimum size, preferred size and stretch factors (they were not present before introducing InternalScrollView either, but are added now for consistency with the other styles).
265 lines
No EOL
8.1 KiB
Text
265 lines
No EOL
8.1 KiB
Text
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
|
|
|
import { LineEditInner } from "../common/common.slint";
|
|
import { ScrollView } from "std-widgets-impl.slint";
|
|
import { StateLayer } from "components.slint";
|
|
import { Palette, Icons } from "styling.slint";
|
|
|
|
component TableViewColumn inherits Rectangle {
|
|
callback clicked <=> i-state-layer.clicked;
|
|
callback adjust_size(length);
|
|
|
|
in property <SortOrder> sort-order: SortOrder.unsorted;
|
|
|
|
i-state-layer := StateLayer {
|
|
background: Palette.primary;
|
|
selection-background: Palette.secondary-container;
|
|
ripple-color: Palette.primary-ripple;
|
|
has-ripple: true;
|
|
}
|
|
|
|
HorizontalLayout {
|
|
padding-left: 16px;
|
|
padding-right: 16px;
|
|
spacing: 4px;
|
|
|
|
HorizontalLayout {
|
|
@children
|
|
}
|
|
|
|
Image {
|
|
visible: root.sort-order != SortOrder.unsorted;
|
|
width: 12px;
|
|
height: 12px;
|
|
y: (parent.height - self.height) / 2;
|
|
source: root.sort-order == SortOrder.ascending ?
|
|
Icons.arrow-upward :
|
|
Icons.arrow-downward;
|
|
colorize: Palette.on-surface;
|
|
}
|
|
}
|
|
|
|
// border
|
|
Rectangle {
|
|
y: parent.height - self.height;
|
|
width: 100%;
|
|
height: 1px;
|
|
background: Palette.outline;
|
|
}
|
|
|
|
Rectangle {
|
|
x: parent.width - 1px;
|
|
width: 1px;
|
|
background: i-movable-touch-area.has-hover ? Palette.outline : transparent;
|
|
|
|
i-movable-touch-area := TouchArea {
|
|
width: 10px;
|
|
|
|
moved => {
|
|
if (self.pressed) {
|
|
adjust_size(self.mouse-x - self.pressed-x);
|
|
}
|
|
}
|
|
mouse-cursor: ew-resize;
|
|
}
|
|
|
|
animate background { duration: 250ms; }
|
|
}
|
|
}
|
|
|
|
component TableViewCell inherits Rectangle {
|
|
clip: true;
|
|
|
|
HorizontalLayout {
|
|
padding-left: 16px;
|
|
padding-right: 16px;
|
|
padding-top: 12px;
|
|
padding-bottom: 12px;
|
|
|
|
@children
|
|
}
|
|
|
|
// border
|
|
Rectangle {
|
|
y: parent.height - self.height;
|
|
width: 100%;
|
|
height: 1px;
|
|
background: Palette.outline;
|
|
}
|
|
}
|
|
|
|
component TableViewRow inherits Rectangle {
|
|
callback clicked <=> i-state-layer.clicked;
|
|
callback pointer-event <=> i-state-layer.pointer-event;
|
|
|
|
in property<bool> selected;
|
|
out property<length> mouse-x <=> i-state-layer.mouse-x;
|
|
out property<length> mouse-y <=> i-state-layer.mouse-y;
|
|
|
|
min-height: max(42px, i-layout.min-height);
|
|
|
|
i-state-layer := StateLayer {
|
|
checked: root.selected;
|
|
background: Palette.primary;
|
|
selection-background: Palette.secondary-container;
|
|
ripple-color: Palette.primary-ripple;
|
|
has-ripple: true;
|
|
}
|
|
|
|
i-layout := HorizontalLayout {
|
|
@children
|
|
}
|
|
|
|
states [
|
|
selected when root.selected : {
|
|
i-state-layer.background: Palette.secondary-container;
|
|
}
|
|
]
|
|
}
|
|
|
|
export component StandardTableView {
|
|
private property <length> item-height: i-scroll-view.viewport-height / rows.length;
|
|
private property <length> current-item-y: i-scroll-view.viewport-y + current-row * item-height;
|
|
private property <length> min-header-height: 42px;
|
|
|
|
callback sort-ascending(int /* column-index */);
|
|
callback sort-descending(int /* column-index */);
|
|
callback row-pointer-event(int /* row-index */, PointerEvent /* event */, Point /* absolute mouse position */);
|
|
callback current-row-changed(int /* current-row */);
|
|
|
|
in property <[[StandardListViewItem]]> rows;
|
|
out property <int> current-sort-column: -1;
|
|
in-out property <[TableColumn]> columns;
|
|
in-out property<int> current-row: -1;
|
|
|
|
min-width: 400px;
|
|
min-height: 200px;
|
|
horizontal-stretch: 1;
|
|
vertical-stretch: 1;
|
|
|
|
VerticalLayout {
|
|
Rectangle {
|
|
clip: true;
|
|
vertical-stretch: 0;
|
|
min-height: i-header-layout.min-height;
|
|
|
|
i-header-layout := HorizontalLayout {
|
|
padding-right: 20px;
|
|
min-height: root.min-header-height;
|
|
vertical-stretch: 0;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
i-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 <bool> 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);
|
|
}
|
|
|
|
pointer-event(pe) => {
|
|
root.row-pointer-event(idx, pe, {
|
|
x: self.absolute-position.x + self.mouse-x - root.absolute-position.x,
|
|
y: self.absolute-position.y + self.mouse-y - root.absolute-position.y,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
public function set-current-row(index: int) {
|
|
if(index < 0 || index >= rows.length) {
|
|
return;
|
|
}
|
|
|
|
current-row = index;
|
|
current-row-changed(current-row);
|
|
|
|
if(current-item-y < 0) {
|
|
i-scroll-view.viewport-y += 0 - current-item-y;
|
|
}
|
|
|
|
if(current-item-y + item-height > i-scroll-view.visible-height) {
|
|
i-scroll-view.viewport-y -= current-item-y + item-height - i-scroll-view.visible-height;
|
|
}
|
|
}
|
|
} |