// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial // cSpell: ignore combobox spinbox import { LineEditInner, TextEdit, AboutSlint } from "../common/common.slint"; import { StyleMetrics, ScrollView } from "std-widgets-impl.slint"; export { StyleMetrics, ScrollView, TextEdit, AboutSlint } export component Button { in property text <=> native.text; out property has-focus <=> native.has-focus; out property pressed <=> native.pressed; in property enabled <=> native.enabled; in property checkable <=> native.checkable; in-out property checked <=> native.checked; in property icon <=> native.icon; callback clicked <=> native.clicked; accessible-checkable: root.checkable; accessible-checked: root.checked; accessible-label: root.text; accessible-role: button; HorizontalLayout { native := NativeButton { checkable: false; enabled: true; } } } export component StandardButton { in property kind <=> native.standard-button-kind; out property has-focus <=> native.has-focus; out property pressed <=> native.pressed; in property enabled <=> native.enabled; callback clicked <=> native.clicked; accessible-label: native.text; accessible-role: button; HorizontalLayout { native := NativeButton { is-standard-button: true; checkable: false; } } } export component CheckBox inherits NativeCheckBox { accessible-checkable: true; accessible-checked <=> root.checked; accessible-label <=> root.text; accessible-role: checkbox; } export component SpinBox inherits NativeSpinBox { accessible-role: spinbox; accessible-value: root.value; accessible-value-minimum: root.minimum; accessible-value-maximum: root.maximum; accessible-value-step: (root.maximum - root.minimum) / 100; } export component Slider inherits NativeSlider { accessible-role: slider; accessible-value: root.value; accessible-value-minimum: root.minimum; accessible-value-maximum: root.maximum; accessible-value-step: (root.maximum - root.minimum) / 100; out property has-focus: fs.has-focus; fs := FocusScope { x:0; width: 0px; key-pressed(event) => { if (root.enabled && event.text == Key.RightArrow) { root.value = Math.min(root.value + 1, root.maximum); accept } else if (root.enabled && event.text == Key.LeftArrow) { root.value = Math.max(root.value - 1, root.minimum); accept } else { reject } } } } export component GroupBox { in property title <=> native.title; in property enabled <=> native.enabled; native := NativeGroupBox { GridLayout { padding-left: native.native-padding-left; padding-right: native.native-padding-right; padding-top: native.native-padding-top; padding-bottom: native.native-padding-bottom; @children } } } export component LineEdit { in property font-size <=> inner.font-size; in-out property text <=> inner.text; in property placeholder-text <=> inner.placeholder-text; in property input-type <=> inner.input-type; in property horizontal-alignment <=> inner.horizontal-alignment; in property read-only <=> inner.read-only; out property has-focus <=> inner.has-focus; in property enabled: true; forward-focus: inner; callback accepted <=> inner.accepted; callback edited <=> inner.edited; horizontal-stretch: 1; vertical-stretch: 0; native := NativeLineEdit { has-focus <=> root.has-focus; enabled: root.enabled; width: 100%; height: 100%; } HorizontalLayout { padding-left: native.native-padding-left; padding-right: native.native-padding-right; padding-top: native.native-padding-top; padding-bottom: native.native-padding-bottom; inner := LineEditInner { placeholder-color: self.enabled ? StyleMetrics.placeholder-color : StyleMetrics.placeholder-color-disabled; enabled: root.enabled; } } } export component ListView inherits ScrollView { @children } component StandardListViewBase inherits ListView { private property item-height: self.viewport-height / self.model.length; private property current-item-y: self.viewport-y + current-item * item-height; in property<[StandardListViewItem]> model; in-out property current-item: -1; for item[i] in root.model : NativeStandardListViewItem { item: item; index: i; is-selected: root.current-item == i; has-hover: ta.has-hover; ta := TouchArea { clicked => { set-current-item(i); } } } public function set-current-item(index: int) { if(index < 0 || index >= model.length) { return; } root.current-item = index; if(current-item-y < 0) { self.viewport-y += 0 - current-item-y; } if(current-item-y + item-height > self.visible-height) { self.viewport-y -= current-item-y + item-height - self.visible-height; } } } export component StandardListView inherits StandardListViewBase { FocusScope { key-pressed(event) => { if (event.text == Key.UpArrow) { root.set-current-item(root.current-item - 1); return accept; } else if (event.text == Key.DownArrow) { root.set-current-item(root.current-item + 1); return accept; } reject } } } export component ComboBox inherits NativeComboBox { in property <[string]> model; in-out property current-index : -1; out property has-focus <=> fs.has-focus; enabled: true; callback selected(string); forward-focus: fs; accessible-role: combobox; accessible-value <=> root.current-value; popup := PopupWindow { x:0; Rectangle { background: NativeStyleMetrics.window-background; } NativeComboBoxPopup { width: 100%; height: 100%; } y: root.height; width: root.width; VerticalLayout { spacing: 0px; for value[i] in root.model: NativeStandardListViewItem { item: { text: value }; is-selected: root.current-index == i; has-hover: ta.has-hover; ta := TouchArea { clicked => { if (root.enabled) { root.current-index = i; root.current-value = value; root.selected(root.current-value); } //is-open = false; } } } } } fs := FocusScope { key-pressed(event) => { if (event.text == Key.UpArrow) { root.current-index = Math.max(root.current-index - 1, 0); root.current-value = root.model[root.current-index]; return accept; } else if (event.text == Key.DownArrow) { root.current-index = Math.min(root.current-index + 1, root.model.length - 1); root.current-value = root.model[root.current-index]; return accept; // PopupWindow can not get hidden again at this time, so do not allow to pop that up. // } else if (event.text == Key.Return) { // touch.clicked() // return accept; } return reject; } touch := TouchArea { enabled <=> root.enabled; clicked => { root.focus(); popup.show(); } } } } export component TabWidgetImpl inherits NativeTabWidget { } export component TabImpl inherits NativeTab { accessible-role: tab; accessible-label <=> root.title; } export component TabBarImpl { // injected properties: in-out property current; // The currently selected tab in-out property current-focused: fs.has-focus ? root.current : -1; // The currently focused tab in-out property num-tabs; // The total number of tabs accessible-role: tab; accessible-delegate-focus: root.current; Rectangle { clip: true; // The breeze style draws outside of the tab bar, which is clip by default with Qt HorizontalLayout { spacing: 0px; // Qt renders Tabs next to each other and renders "spacing" as part of the tab itself alignment: NativeStyleMetrics.tab-bar-alignment; @children } } fs := FocusScope { x:0; width: 0px; // Do not react on clicks key-pressed(event) => { if (event.text == Key.LeftArrow) { root.current = Math.max(root.current - 1, 0); return accept; } if (event.text == Key.RightArrow) { root.current = Math.min(root.current + 1, root.num-tabs - 1); return accept; } return reject; } } } export component TabWidget inherits TabWidget {} export component VerticalBox inherits VerticalLayout { spacing: NativeStyleMetrics.layout-spacing; padding: NativeStyleMetrics.layout-spacing; } export component HorizontalBox inherits HorizontalLayout { spacing: NativeStyleMetrics.layout-spacing; padding: NativeStyleMetrics.layout-spacing; } export component GridBox inherits GridLayout { spacing: NativeStyleMetrics.layout-spacing; padding: NativeStyleMetrics.layout-spacing; } export component StandardTableView { callback sort-ascending(int); callback sort-descending(int); out property current-sort-column: -1; in-out property <[TableColumn]> columns; in property <[[StandardListViewItem]]> rows; horizontal-stretch: 1; vertical-stretch: 1; function sort(index: int) { if (current-sort-column != index) { columns[current-sort-column].sort-order = SortOrder.unsorted; } if(columns[index].sort-order == SortOrder.ascending) { columns[index].sort-order = SortOrder.descending; sort-descending(index); } else { columns[index].sort-order = SortOrder.ascending; sort-ascending(index); } current-sort-column = index; } scroll-view := NativeScrollView { vertical-max: fli.viewport-height > fli.height ? fli.viewport-height - fli.height : 0phx; vertical-page-size: fli.height; horizontal-max: fli.viewport-width > fli.width ? fli.viewport-width - fli.width : 0phx; horizontal-page-size: fli.width; fli := Flickable { x: scroll-view.native-padding-left; width: scroll-view.width - scroll-view.native-padding-left - scroll-view.native-padding-right; y: scroll-view.native-padding-top + header.height; height: scroll-view.height - self.y - scroll-view.native-padding-bottom; interactive: false; viewport-y <=> scroll-view.vertical-value; viewport-x <=> scroll-view.horizontal-value; VerticalLayout { alignment: start; for row[i] in rows : Rectangle { width: max(row-layout.preferred-width, fli.width); row-ta := TouchArea {} row-layout := HorizontalLayout { for cell[index] in row : Rectangle { horizontal-stretch: 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; HorizontalLayout { NativeStandardListViewItem { item: cell; index: i; //is-selected: root.current-item == i; has-hover: row-ta.has-hover; } } } } } } } } header := Rectangle { clip: true; height: header-layout.preferred-height; x: scroll-view.native-padding-left; y: scroll-view.native-padding-top; width: fli.width; header-layout := HorizontalLayout { width: max(self.preferred-width, parent.width); x: fli.viewport-x; for column[index] in columns : NativeTableHeaderSection { item: column; 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; TouchArea { clicked => { sort(index); } } TouchArea { width: 10px; x: parent.width - self.width / 2; moved => { if (self.pressed) { column.width = max(1px, parent.width + (self.mouse-x - self.pressed-x)); } } mouse-cursor: ew-resize; } } } } }