Made ComboBox scrollable (#5581)

This commit is contained in:
Florian Blasius 2024-07-12 13:14:00 +00:00 committed by GitHub
parent 16996dfa16
commit 806d12bcb3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 214 additions and 158 deletions

View file

@ -40,6 +40,7 @@ All notable changes to this project are documented in this file.
- Added `step-size` to `SpinBox` - Added `step-size` to `SpinBox`
- Added `TimePickerPopup` and `DatePickerPopup`. - Added `TimePickerPopup` and `DatePickerPopup`.
- Fixed accessible value and actions on ProgressIndicator, Spinner, Spinbox, CheckBox, Switch - Fixed accessible value and actions on ProgressIndicator, Spinner, Spinbox, CheckBox, Switch
- Made `ComboBox` scrollable
### C++ API ### C++ API

View file

@ -1,9 +1,10 @@
// Copyright © SixtyFPS GmbH <info@slint.dev> // Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
import { CosmicFontSettings, CosmicPalette, Icons } from "styling.slint"; import { CosmicFontSettings, CosmicPalette, Icons, CosmicSizeSettings } from "styling.slint";
import { MenuBorder, ListItem, StateLayerBase } from "components.slint"; import { MenuBorder, ListItem, StateLayerBase } from "components.slint";
import { ComboBoxBase } from "../common/combobox-base.slint"; import { ComboBoxBase } from "../common/combobox-base.slint";
import { ScrollView } from "./scrollview.slint";
export component ComboBox { export component ComboBox {
in property <[string]> model <=> base.model; in property <[string]> model <=> base.model;
@ -14,6 +15,9 @@ export component ComboBox {
callback selected <=> base.selected; callback selected <=> base.selected;
property <length> popup-padding: 4px;
property <int> visible-items: 6;
min-width: max(160px, layout.min-height); min-width: max(160px, layout.min-height);
min-height: max(32px, layout.min-height); min-height: max(32px, layout.min-height);
horizontal-stretch: 1; horizontal-stretch: 1;
@ -78,12 +82,17 @@ export component ComboBox {
popup := PopupWindow { popup := PopupWindow {
x: 0; x: 0;
// Position the popup so that the first element is over the popup.
// Ideally it should be so that the current element is over the popup.
y: root.height + 4px; y: root.height + 4px;
width: root.width; width: root.width;
height: root.visible-items * CosmicSizeSettings.item-height + 2 * root.popup-padding;
MenuBorder { MenuBorder {
ScrollView {
VerticalLayout { VerticalLayout {
padding: 8px; alignment: start;
padding: root.popup-padding;
for value[index] in root.model : ListItem { for value[index] in root.model : ListItem {
item: { text: value }; item: { text: value };
@ -100,4 +109,5 @@ export component ComboBox {
} }
} }
} }
} }
}

View file

@ -1,7 +1,7 @@
// Copyright © SixtyFPS GmbH <info@slint.dev> // Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
import { Icons, CosmicPalette, CosmicFontSettings } from "styling.slint"; import { Icons, CosmicPalette, CosmicFontSettings, CosmicSizeSettings } from "styling.slint";
export component StateLayerBase { export component StateLayerBase {
in property <length> border-radius <=> overlay.border-radius; in property <length> border-radius <=> overlay.border-radius;
@ -99,7 +99,7 @@ export component ListItem {
in property <length> pressed-y; in property <length> pressed-y;
min-width: layout.min-width; min-width: layout.min-width;
min-height: max(40px, layout.min-height); min-height: max(CosmicSizeSettings.item-height, layout.min-height);
vertical-stretch: 0; vertical-stretch: 0;
horizontal-stretch: 1; horizontal-stretch: 1;

View file

@ -81,3 +81,7 @@ export global Icons {
out property <image> edit: @image-url("_edit.svg"); out property <image> edit: @image-url("_edit.svg");
out property <image> calendar: @image-url("_calendar.svg"); out property <image> calendar: @image-url("_calendar.svg");
} }
export global CosmicSizeSettings {
out property <length> item-height: 40px;
}

View file

@ -1,46 +1,49 @@
// Copyright © SixtyFPS GmbH <info@slint.dev> // Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
import { CupertinoFontSettings, CupertinoPalette, Icons } from "styling.slint"; import { CupertinoFontSettings, CupertinoPalette, Icons, CupertinoSizeSettings } from "styling.slint";
import { MenuBorder, ListItem, FocusBorder } from "components.slint"; import { MenuBorder, ListItem, FocusBorder } from "components.slint";
import { ComboBoxBase } from "../common/combobox-base.slint"; import { ComboBoxBase } from "../common/combobox-base.slint";
import { ScrollView } from "./scrollview.slint";
export component ComboBox { export component ComboBox {
in property <[string]> model <=> i-base.model; in property <[string]> model <=> base.model;
in property <bool> enabled <=> i-base.enabled; in property <bool> enabled <=> base.enabled;
out property <bool> has-focus <=> i-base.has-focus; out property <bool> has-focus <=> base.has-focus;
in-out property <int> current-index <=> i-base.current-index; in-out property <int> current-index <=> base.current-index;
in-out property <string> current-value <=> i-base.current-value; in-out property <string> current-value <=> base.current-value;
callback selected <=> i-base.selected; callback selected <=> base.selected;
private property <brush> background: CupertinoPalette.control-background; property <brush> background: CupertinoPalette.control-background;
property <length> popup-padding: 4px;
property <int> visible-items: 6;
min-width: max(160px, i-layout.min-width); min-width: max(160px, layout.min-width);
min-height: max(22px, i-layout.min-height); min-height: max(22px, layout.min-height);
horizontal-stretch: 1; horizontal-stretch: 1;
vertical-stretch: 0; vertical-stretch: 0;
forward-focus: i-base; forward-focus: base;
accessible-role: combobox; accessible-role: combobox;
states [ states [
disabled when !root.enabled : { disabled when !root.enabled : {
i-text.color: CupertinoPalette.foreground-secondary; text.color: CupertinoPalette.foreground-secondary;
i-top-icon.colorize: CupertinoPalette.foreground-secondary; top-icon.colorize: CupertinoPalette.foreground-secondary;
i-bottom-icon.colorize: CupertinoPalette.foreground-secondary; bottom-icon.colorize: CupertinoPalette.foreground-secondary;
root.background: CupertinoPalette.tertiary-control-background; root.background: CupertinoPalette.tertiary-control-background;
} }
pressed when i-base.pressed : { pressed when base.pressed : {
root.background: CupertinoPalette.secondary-control-background; root.background: CupertinoPalette.secondary-control-background;
} }
] ]
i-base := ComboBoxBase { base := ComboBoxBase {
width: 100%; width: 100%;
height: 100%; height: 100%;
show-popup => { show-popup => {
i-popup.show(); popup.show();
} }
} }
@ -72,14 +75,14 @@ export component ComboBox {
} }
} }
i-layout := HorizontalLayout { layout := HorizontalLayout {
padding-left: 8px; padding-left: 8px;
padding-right: 8px; padding-right: 8px;
padding-top: 4px; padding-top: 4px;
padding-bottom: 4px; padding-bottom: 4px;
spacing: 4px; spacing: 4px;
i-text := Text { text := Text {
horizontal-alignment: left; horizontal-alignment: left;
vertical-alignment: center; vertical-alignment: center;
font-size: CupertinoFontSettings.body.font-size; font-size: CupertinoFontSettings.body.font-size;
@ -129,13 +132,13 @@ export component ComboBox {
padding: 4px; padding: 4px;
spacing: 4px; spacing: 4px;
i-top-icon := Image { top-icon := Image {
x: (parent.width - self.width) / 2; x: (parent.width - self.width) / 2;
colorize: CupertinoPalette.accent-foreground; colorize: CupertinoPalette.accent-foreground;
source: Icons.chevron-up; source: Icons.chevron-up;
} }
i-bottom-icon := Image { bottom-icon := Image {
x: (parent.width - self.width) / 2; x: (parent.width - self.width) / 2;
colorize: CupertinoPalette.accent-foreground; colorize: CupertinoPalette.accent-foreground;
source: Icons.chevron-down; source: Icons.chevron-down;
@ -144,27 +147,31 @@ export component ComboBox {
} }
} }
i-popup := PopupWindow { popup := PopupWindow {
x: 0; x: 0;
y: parent.height + 6px; y: parent.height + 6px;
min-width: root.width; width: root.width;
height: root.visible-items * CupertinoSizeSettings.item-height + 2 * root.popup-padding;
MenuBorder { MenuBorder {
ScrollView {
VerticalLayout { VerticalLayout {
padding: 4px; alignment: start;
padding: root.popup-padding;
for value[index] in root.model : ListItem { for value[index] in root.model : ListItem {
padding-horizontal: 0; padding-horizontal: 0;
item: { text: value }; item: { text: value };
is-selected: index == root.current-index; is-selected: index == root.current-index;
has-hover: i-touch-area.has-hover; has-hover: touch-area.has-hover;
pressed: i-touch-area.pressed; pressed: touch-area.pressed;
pressed-x: i-touch-area.pressed-x; pressed-x: touch-area.pressed-x;
pressed-y: i-touch-area.pressed-y; pressed-y: touch-area.pressed-y;
i-touch-area := TouchArea { touch-area := TouchArea {
clicked => { clicked => {
i-base.select(index); base.select(index);
}
} }
} }
} }

View file

@ -1,7 +1,7 @@
// Copyright © SixtyFPS GmbH <info@slint.dev> // Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
import { CupertinoPalette, CupertinoFontSettings, Icons } from "styling.slint"; import { CupertinoPalette, CupertinoFontSettings, Icons, CupertinoSizeSettings } from "styling.slint";
export component FocusBorder inherits Rectangle { export component FocusBorder inherits Rectangle {
in property <bool> has-focus; in property <bool> has-focus;
@ -55,7 +55,7 @@ export component ListItem {
in property <length> pressed-y; in property <length> pressed-y;
min-width: i-layout.min-width; min-width: i-layout.min-width;
min-height: max(22px, i-layout.min-height); min-height: max(CupertinoSizeSettings.item-height, i-layout.min-height);
vertical-stretch: 0; vertical-stretch: 0;
horizontal-stretch: 1; horizontal-stretch: 1;

View file

@ -100,3 +100,7 @@ export global Icons {
out property <image> edit: @image-url("_edit.svg"); out property <image> edit: @image-url("_edit.svg");
out property <image> calendar: @image-url("_calendar.svg"); out property <image> calendar: @image-url("_calendar.svg");
} }
export global CupertinoSizeSettings {
out property <length> item-height: 22px;
}

View file

@ -1,55 +1,59 @@
// Copyright © SixtyFPS GmbH <info@slint.dev> // Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
import { FluentFontSettings, FluentPalette, Icons } from "styling.slint"; import { FluentFontSettings, FluentPalette, Icons, FluentSizeSettings } from "styling.slint";
import { MenuBorder, ListItem, FocusBorder } from "components.slint"; import { MenuBorder, ListItem, FocusBorder } from "components.slint";
import { ComboBoxBase } from "../common/combobox-base.slint"; import { ComboBoxBase } from "../common/combobox-base.slint";
import { ScrollView } from "./scrollview.slint";
export component ComboBox { export component ComboBox {
in property <[string]> model <=> i-base.model; in property <[string]> model <=> base.model;
in property <bool> enabled <=> i-base.enabled; in property <bool> enabled <=> base.enabled;
out property <bool> has-focus <=> i-base.has-focus; out property <bool> has-focus <=> base.has-focus;
in-out property <int> current-index <=> i-base.current-index; in-out property <int> current-index <=> base.current-index;
in-out property <string> current-value <=> i-base.current-value; in-out property <string> current-value <=> base.current-value;
callback selected <=> i-base.selected; callback selected <=> base.selected;
min-width: max(160px, i-layout.min-height); property <length> popup-padding: 4px;
min-height: max(32px, i-layout.min-height); property <int> visible-items: 6;
min-width: max(160px, layout.min-height);
min-height: max(32px, layout.min-height);
horizontal-stretch: 1; horizontal-stretch: 1;
vertical-stretch: 0; vertical-stretch: 0;
forward-focus: i-base; forward-focus: base;
accessible-role: combobox; accessible-role: combobox;
states [ states [
disabled when !root.enabled : { disabled when !root.enabled : {
i-background.background: FluentPalette.control-disabled; background.background: FluentPalette.control-disabled;
i-background.border-color: FluentPalette.border; background.border-color: FluentPalette.border;
i-text.color: FluentPalette.text-disabled; text.color: FluentPalette.text-disabled;
i-icon.colorize: FluentPalette.text-disabled; icon.colorize: FluentPalette.text-disabled;
} }
pressed when i-base.pressed : { pressed when base.pressed : {
i-background.background: FluentPalette.control-alt-tertiary; background.background: FluentPalette.control-alt-tertiary;
i-background.border-color: FluentPalette.border; background.border-color: FluentPalette.border;
i-text.color: FluentPalette.text-secondary; text.color: FluentPalette.text-secondary;
i-icon.colorize: FluentPalette.text-tertiary; icon.colorize: FluentPalette.text-tertiary;
} }
hover when i-base.has-hover : { hover when base.has-hover : {
i-background.background: FluentPalette.control-secondary; background.background: FluentPalette.control-secondary;
} }
] ]
i-base := ComboBoxBase { base := ComboBoxBase {
width: 100%; width: 100%;
height: 100%; height: 100%;
show-popup => { show-popup => {
i-popup.show(); popup.show();
} }
} }
i-background := Rectangle { background := Rectangle {
border-radius: 3px; border-radius: 3px;
background: FluentPalette.control-background; background: FluentPalette.control-background;
border-width: 1px; border-width: 1px;
@ -57,12 +61,12 @@ export component ComboBox {
animate border-color { duration: 200ms; } animate border-color { duration: 200ms; }
i-layout := HorizontalLayout { layout := HorizontalLayout {
padding-left: 11px; padding-left: 11px;
padding-right: 11px; padding-right: 11px;
spacing: 8px; spacing: 8px;
i-text := Text { text := Text {
horizontal-alignment: left; horizontal-alignment: left;
vertical-alignment: center; vertical-alignment: center;
font-size: FluentFontSettings.body.font-size; font-size: FluentFontSettings.body.font-size;
@ -71,7 +75,7 @@ export component ComboBox {
text: root.current-value; text: root.current-value;
} }
i-icon := Image { icon := Image {
colorize: FluentPalette.text-secondary; colorize: FluentPalette.text-secondary;
width: 12px; width: 12px;
source: Icons.dropdown; source: Icons.dropdown;
@ -83,30 +87,34 @@ export component ComboBox {
} }
// focus border // focus border
if (root.has-focus && root.enabled) : FocusBorder { if root.has-focus && root.enabled : FocusBorder {
border-radius: i-background.border-radius; border-radius: background.border-radius;
} }
i-popup := PopupWindow { popup := PopupWindow {
x: 0; x: 0;
// Position the popup so that the first element is over the popup. // Position the popup so that the first element is over the popup.
// Ideally it should be so that the current element is over the popup. // Ideally it should be so that the current element is over the popup.
y: -4px; y: -4px;
width: root.width; width: root.width;
height: root.visible-items * FluentSizeSettings.item-height + 2 * root.popup-padding;
MenuBorder { MenuBorder {
ScrollView {
VerticalLayout { VerticalLayout {
padding: 4px; alignment: start;
padding: root.popup-padding;
for value[index] in root.model : ListItem { for value[index] in root.model : ListItem {
item: { text: value }; item: { text: value };
is-selected: index == root.current-index; is-selected: index == root.current-index;
has-hover: i-touch-area.has-hover; has-hover: touch-area.has-hover;
pressed: i-touch-area.pressed; pressed: touch-area.pressed;
i-touch-area := TouchArea { touch-area := TouchArea {
clicked => { clicked => {
i-base.select(index); base.select(index);
}
} }
} }
} }

View file

@ -1,7 +1,7 @@
// Copyright © SixtyFPS GmbH <info@slint.dev> // Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
import { FluentPalette, FluentFontSettings } from "styling.slint"; import { FluentPalette, FluentFontSettings, FluentSizeSettings } from "styling.slint";
export component FocusBorder inherits Rectangle { export component FocusBorder inherits Rectangle {
border-width: 2px; border-width: 2px;
@ -43,7 +43,7 @@ export component ListItem {
in property <length> pressed-y; in property <length> pressed-y;
min-width: i-layout.min-width; min-width: i-layout.min-width;
min-height: max(40px, i-layout.min-height); min-height: max(FluentSizeSettings.item-height, i-layout.min-height);
vertical-stretch: 0; vertical-stretch: 0;
horizontal-stretch: 1; horizontal-stretch: 1;

View file

@ -111,3 +111,7 @@ export global Icons {
out property <image> edit: @image-url("_edit.svg"); out property <image> edit: @image-url("_edit.svg");
out property <image> calendar: @image-url("_calendar.svg"); out property <image> calendar: @image-url("_calendar.svg");
} }
export global FluentSizeSettings {
out property <length> item-height: 40px;
}

View file

@ -2,51 +2,54 @@
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
import { MaterialPalette, MaterialFontSettings, Elevation, Icons } from "styling.slint"; import { MaterialPalette, MaterialFontSettings, Elevation, Icons, MaterialSizeSettings } from "styling.slint";
import { ListItem, StateLayer } from "components.slint"; import { ListItem, StateLayer } from "components.slint";
import { ComboBoxBase } from "../common/combobox-base.slint"; import { ComboBoxBase } from "../common/combobox-base.slint";
import { ScrollView } from "./scrollview.slint";
export component ComboBox { export component ComboBox {
in property <[string]> model <=> i-base.model; in property <[string]> model <=> base.model;
in property <bool> enabled <=> i-base.enabled; in property <bool> enabled <=> base.enabled;
out property <bool> has-focus <=> i-base.has-focus; out property <bool> has-focus <=> base.has-focus;
in-out property <int> current-index <=> i-base.current-index; in-out property <int> current-index <=> base.current-index;
in-out property <string> current-value <=> i-base.current-value; in-out property <string> current-value <=> base.current-value;
callback selected <=> i-base.selected; callback selected <=> base.selected;
min-width: max(160px, i-layout.min-width); property <int> visible-items: 6;
min-height: max(22px, i-layout.min-height);
min-width: max(160px, layout.min-width);
min-height: max(22px, layout.min-height);
horizontal-stretch: 1; horizontal-stretch: 1;
vertical-stretch: 0; vertical-stretch: 0;
forward-focus: i-base; forward-focus: base;
accessible-role: combobox; accessible-role: combobox;
states [ states [
disabled when !root.enabled : { disabled when !root.enabled : {
i-background.border-color: MaterialPalette.control-foreground; background.border-color: MaterialPalette.control-foreground;
i-background.opacity: 0.38; background.opacity: 0.38;
i-label.opacity: 0.38; label.opacity: 0.38;
i-icon.opacity: 0.38; icon.opacity: 0.38;
} }
focused when root.has-focus : { focused when root.has-focus : {
i-background.border-width: 2px; background.border-width: 2px;
i-background.border-color: MaterialPalette.accent-background; background.border-color: MaterialPalette.accent-background;
i-label.color: MaterialPalette.accent-background; label.color: MaterialPalette.accent-background;
i-icon.colorize: MaterialPalette.accent-background; icon.colorize: MaterialPalette.accent-background;
} }
] ]
i-base := ComboBoxBase { base := ComboBoxBase {
width: 100%; width: 100%;
height: 100%; height: 100%;
show-popup => { show-popup => {
i-popup.show(); popup.show();
} }
} }
i-background := Rectangle { background := Rectangle {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 4px; border-radius: 4px;
@ -54,12 +57,12 @@ export component ComboBox {
border-color: MaterialPalette.border; border-color: MaterialPalette.border;
} }
i-layout := HorizontalLayout { layout := HorizontalLayout {
padding-left: 16px; padding-left: 16px;
padding-right: 12px; padding-right: 12px;
spacing: 16px; spacing: 16px;
i-label := Text { label := Text {
text <=> root.current-value; text <=> root.current-value;
color: MaterialPalette.control-foreground; color: MaterialPalette.control-foreground;
vertical-alignment: center; vertical-alignment: center;
@ -69,7 +72,7 @@ export component ComboBox {
font-weight: MaterialFontSettings.body-large.font-weight; font-weight: MaterialFontSettings.body-large.font-weight;
} }
i-icon := Image { icon := Image {
width: 24px; width: 24px;
height: 24px; height: 24px;
y: (parent.height - self.height) / 2; y: (parent.height - self.height) / 2;
@ -78,12 +81,13 @@ export component ComboBox {
} }
} }
i-popup := PopupWindow { popup := PopupWindow {
x: 0; x: 0;
y: root.height; y: root.height;
width: root.width; width: root.width;
height: root.visible-items * MaterialSizeSettings.item-height;
i-popup-container := Rectangle { popup-container := Rectangle {
background: MaterialPalette.alternate-background; background: MaterialPalette.alternate-background;
drop-shadow-color: MaterialPalette.shadow; drop-shadow-color: MaterialPalette.shadow;
drop-shadow-blur: Elevation.level2; drop-shadow-blur: Elevation.level2;
@ -91,16 +95,20 @@ export component ComboBox {
border-radius: 4px; border-radius: 4px;
} }
ScrollView {
VerticalLayout { VerticalLayout {
alignment: start;
for value[index] in root.model: ListItem { for value[index] in root.model: ListItem {
item: { text: value }; item: { text: value };
is-selected: index == root.current-index; is-selected: index == root.current-index;
has-hover: i-touch-area.has-hover; has-hover: touch-area.has-hover;
pressed: i-touch-area.pressed; pressed: touch-area.pressed;
i-touch-area := StateLayer { touch-area := StateLayer {
clicked => { clicked => {
i-base.select(index); base.select(index);
}
} }
} }
} }

View file

@ -1,7 +1,7 @@
// Copyright © SixtyFPS GmbH <info@slint.dev> // Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
import { MaterialPalette, MaterialFontSettings } from "styling.slint"; import { MaterialPalette, MaterialFontSettings, MaterialSizeSettings } from "styling.slint";
export component Ripple inherits Rectangle { export component Ripple inherits Rectangle {
in property <length> ripple-x; in property <length> ripple-x;
@ -101,7 +101,7 @@ export component ListItem {
in property <length> pressed-y; in property <length> pressed-y;
min-width: i-layout.min-width; min-width: i-layout.min-width;
min-height: max(48px, i-layout.min-height); min-height: max(MaterialSizeSettings.item-height, i-layout.min-height);
vertical-stretch: 0; vertical-stretch: 0;
horizontal-stretch: 1; horizontal-stretch: 1;

View file

@ -83,3 +83,7 @@ export global Icons {
out property <image> edit: @image-url("_edit.svg"); out property <image> edit: @image-url("_edit.svg");
out property <image> calendar: @image-url("_calendar.svg"); out property <image> calendar: @image-url("_calendar.svg");
} }
export global MaterialSizeSettings {
out property <length> item-height: 48px;
}

View file

@ -2,48 +2,53 @@
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
import { ComboBoxBase } from "../common/combobox-base.slint"; import { ComboBoxBase } from "../common/combobox-base.slint";
import { ScrollView } from "./scrollview.slint";
export component ComboBox { export component ComboBox {
in property <[string]> model <=> i-base.model; in property <[string]> model <=> base.model;
in property <bool> enabled <=> i-base.enabled; in property <bool> enabled <=> base.enabled;
out property <bool> has-focus <=> i-base.has-focus; out property <bool> has-focus <=> base.has-focus;
in-out property <int> current-index <=> i-base.current-index; in-out property <int> current-index <=> base.current-index;
in-out property <string> current-value <=> i-base.current-value; in-out property <string> current-value <=> base.current-value;
callback selected <=> i-base.selected; callback selected <=> base.selected;
property <length> popup-height: 224px;
accessible-role: combobox; accessible-role: combobox;
accessible-value <=> root.current-value; accessible-value <=> root.current-value;
forward-focus: i-base; forward-focus: base;
HorizontalLayout { HorizontalLayout {
i-native := NativeComboBox { native := NativeComboBox {
current-value <=> root.current-value; current-value <=> root.current-value;
has-focus <=> root.has-focus; has-focus <=> root.has-focus;
enabled <=> root.enabled; enabled <=> root.enabled;
} }
} }
i-base := ComboBoxBase { base := ComboBoxBase {
width: 100%; width: 100%;
height: 100%; height: 100%;
show-popup => { show-popup => {
i-popup.show(); popup.show();
} }
} }
i-popup := PopupWindow { popup := PopupWindow {
x: 0; x: 0;
y: root.height; y: root.height;
width: root.width; width: root.width;
height: root.popup-height;
NativeComboBoxPopup { NativeComboBoxPopup {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
ScrollView {
VerticalLayout { VerticalLayout {
spacing: 0px; alignment: start;
for value[index] in root.model: NativeStandardListViewItem { for value[index] in root.model: NativeStandardListViewItem {
item: { text: value }; item: { text: value };
@ -53,7 +58,8 @@ export component ComboBox {
ta := TouchArea { ta := TouchArea {
clicked => { clicked => {
i-base.select(index); base.select(index);
}
} }
} }
} }