mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-16 00:20:43 +00:00

In the compiler this is still very primitive, but an attempt to start a generic interface. The basic assumption is that all item functions will eventually need access to the window adapter and itemrc. Support for additional arguments is still missing. Also missing is support for the function access via rtti in the interpreter, hence the hardcoding at the moment.
744 lines
24 KiB
Text
744 lines
24 KiB
Text
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
|
|
|
// cSpell: ignore combobox spinbox standardbutton
|
|
|
|
import { LineEditInner, TextEdit, AboutSlint } from "../common/common.slint";
|
|
import { StandardButton } from "../common/standardbutton.slint";
|
|
import { StyleMetrics, ScrollView, Button, Palette, Switch } from "std-widgets-impl.slint";
|
|
export { StyleMetrics, ScrollView, Button, StandardButton, TextEdit, AboutSlint, Switch }
|
|
export * from "widget-table-view.slint";
|
|
|
|
export component CheckBox {
|
|
callback toggled;
|
|
in property <string> text <=> text.text;
|
|
in-out property <bool> checked;
|
|
out property <bool> has-focus: fs.has-focus;
|
|
in property<bool> enabled: true;
|
|
min-height: 20px;
|
|
horizontal-stretch: 0;
|
|
vertical-stretch: 0;
|
|
|
|
accessible-label <=> text.text;
|
|
accessible-checkable: true;
|
|
accessible-checked <=> root.checked;
|
|
accessible-role: checkbox;
|
|
|
|
HorizontalLayout {
|
|
spacing: 8px;
|
|
|
|
VerticalLayout {
|
|
alignment: center;
|
|
Rectangle {
|
|
border-width: 1px;
|
|
border-radius: 2px;
|
|
/* border-color: !enabled ? Palette.neutralLighter : Palette.neutralSecondaryAlt;
|
|
background: !enabled ? Palette.white
|
|
: touch.pressed ? Palette.neutralLight
|
|
: touch.has-hover ? Palette.neutralLighter
|
|
: Palette.themePrimary;*/
|
|
|
|
border-color: root.checked ? self.background : !root.enabled ? Palette.neutralTertiaryAlt : Palette.neutralSecondaryAlt;
|
|
background: !root.checked ? Palette.white
|
|
: !root.enabled ? Palette.neutralTertiaryAlt
|
|
: touch.has-hover || touch.pressed ? Palette.themeDark
|
|
: Palette.themePrimary;
|
|
animate background { duration: 250ms; easing: ease; }
|
|
|
|
//width: height;
|
|
vertical-stretch: 0;
|
|
width: 20px;
|
|
height: 20px;
|
|
|
|
if (root.checked || touch.has-hover || touch.pressed) : Path {
|
|
width: 66%;
|
|
height: 66%;
|
|
x: (parent.width - self.width) / 2;
|
|
y: (parent.height - self.height) / 2;
|
|
commands: "M.22.5.42.7.78.34.74.3.42.62.26.54z";
|
|
fill: root.checked ? Palette.white : Palette.neutralSecondaryAlt;
|
|
}
|
|
}
|
|
}
|
|
|
|
text := Text {
|
|
color: !root.enabled ? Palette.neutralTertiary : Palette.neutralDark;
|
|
horizontal-alignment: left;
|
|
vertical-alignment: center;
|
|
vertical-stretch: 1;
|
|
}
|
|
|
|
}
|
|
|
|
touch := TouchArea {
|
|
enabled <=> root.enabled;
|
|
clicked => {
|
|
if (root.enabled) {
|
|
root.checked = !root.checked;
|
|
root.toggled();
|
|
}
|
|
}
|
|
}
|
|
|
|
fs := FocusScope {
|
|
x:0;
|
|
width: 0px; // Do not react on clicks
|
|
enabled <=> root.enabled;
|
|
|
|
key-pressed(event) => {
|
|
if (event.text == " " || event.text == "\n") {
|
|
touch.clicked();
|
|
return accept;
|
|
}
|
|
return reject;
|
|
}
|
|
}
|
|
|
|
Rectangle { // Focus rectangle
|
|
x: -3px;
|
|
y: self.x;
|
|
width: parent.width - 2*self.x;
|
|
height: parent.height - 2*self.y;
|
|
border-width: root.enabled && root.has-focus ? 1px : 0px;
|
|
border-color: Palette.black;
|
|
}
|
|
}
|
|
|
|
component SpinBoxButton inherits Rectangle {
|
|
callback clicked <=> touch.clicked;
|
|
in-out property<bool> enabled <=> touch.enabled;
|
|
background: !root.enabled ? transparent
|
|
: touch.pressed ? Palette.neutralLight
|
|
: touch.has-hover ? Palette.neutralLighter
|
|
: Palette.white;
|
|
|
|
in-out property <color> symbol-color: !root.enabled ? Palette.neutralTertiary
|
|
: touch.pressed || touch.has-hover ? Palette.neutralPrimary
|
|
: Palette.neutralSecondary;
|
|
touch := TouchArea { }
|
|
}
|
|
|
|
export component SpinBox {
|
|
in-out property <int> value;
|
|
in property <int> minimum;
|
|
in property <int> maximum: 100;
|
|
in property <bool> enabled <=> fs.enabled;
|
|
out property <bool> has-focus <=> fs.has-focus;
|
|
forward-focus: fs;
|
|
|
|
min-height: max(32px, l.min-height);
|
|
horizontal-stretch: 1;
|
|
vertical-stretch: 0;
|
|
|
|
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;
|
|
|
|
fs := FocusScope {
|
|
|
|
Rectangle {
|
|
background: !root.enabled ? Palette.neutralLighter : Palette.white;
|
|
}
|
|
|
|
l := GridLayout {
|
|
padding-left: 8px;
|
|
padding-top: 3px;
|
|
padding-bottom: 3px;
|
|
text := Text {
|
|
rowspan: 2;
|
|
text: root.value;
|
|
color: !root.enabled ? Palette.neutralTertiary : Palette.neutralDark;
|
|
horizontal-alignment: left;
|
|
vertical-alignment: center;
|
|
}
|
|
Rectangle { width: 8px; }
|
|
button := SpinBoxButton {
|
|
width: 25px;
|
|
enabled: root.enabled;
|
|
Path {
|
|
commands: "M978.2,688.9l-84.2,82.1c-15.7,15.3-41.1,15.3-56.7,0l-341-304.2L162.6,764.5c-15.5,15.1-41,15.1-56.6,0l-84.3-82.1c-15.6-15.2-15.6-39.9,0-55.2l446.6-398.2c15.7-15.3,41-15.3,56.7,0l6.9,6.7l446.3,398.1C993.9,649,993.9,673.7,978.2,688.9z";
|
|
fill: parent.symbol-color;
|
|
height: 33%;
|
|
x: (parent.width - self.width) / 2;
|
|
y: (parent.height - self.height) / 2;
|
|
}
|
|
clicked => {
|
|
if (root.value < root.maximum) {
|
|
root.value += 1;
|
|
}
|
|
root.focus();
|
|
}
|
|
}
|
|
SpinBoxButton {
|
|
row: 1; col: 2;
|
|
enabled: root.enabled;
|
|
Path {
|
|
commands: "M21.8,311.1l84.2-82.1c15.7-15.2,41-15.2,56.7,0l341.1,304.1l333.7-297.5c15.5-15.2,41-15.2,56.6,0l84.3,82.1c15.6,15.2,15.6,40,0,55.2L531.7,771c-15.7,15.3-41,15.3-56.7,0l-6.9-6.7L21.8,366.3C6.1,351,6.1,326.3,21.8,311.1z";
|
|
fill: parent.symbol-color;
|
|
height: 33%;
|
|
x: (parent.width - self.width) / 2;
|
|
y: (parent.height - self.height) / 2;
|
|
}
|
|
clicked => {
|
|
if (root.value > root.minimum) {
|
|
root.value -= 1;
|
|
}
|
|
root.focus();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
Rectangle {
|
|
x: root.enabled && root.has-focus ? -2px : 0px;
|
|
y: self.x;
|
|
width: parent.width - 2*self.x;
|
|
height: parent.height - 2*self.y;
|
|
border-radius: 2px;
|
|
border-width: !root.enabled ? 0px : root.has-focus ? 3px : 1px;
|
|
border-color: !root.enabled ? Palette.neutralLighter
|
|
: root.has-focus ? Palette.themeSecondary
|
|
: Palette.neutralDark;
|
|
}
|
|
|
|
key-pressed(event) => {
|
|
if (root.enabled && event.text == Key.UpArrow && root.value < root.maximum) {
|
|
root.value += 1;
|
|
accept
|
|
} else if (root.enabled && event.text == Key.DownArrow && root.value > root.minimum) {
|
|
root.value -= 1;
|
|
accept
|
|
} else {
|
|
reject
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export component ProgressIndicator {
|
|
private property <float> prog: (progress - minimum) / (maximum - minimum);
|
|
|
|
in property<float> maximum: 100;
|
|
in property<float> minimum: 0;
|
|
in property <float> progress;
|
|
in property <bool> indeterminate;
|
|
|
|
min-height: 2px;
|
|
horizontal-stretch: 1;
|
|
vertical-stretch: 0;
|
|
accessible-role: none;
|
|
accessible-value: root.progress;
|
|
accessible-value-minimum: root.minimum;
|
|
accessible-value-maximum: root.maximum;
|
|
|
|
container := Rectangle {
|
|
background: Palette.neutralLight;
|
|
clip: true;
|
|
|
|
track := Rectangle {
|
|
background: !root.indeterminate ? Palette.themePrimary : Palette.primaryGradient;
|
|
x: !root.indeterminate ? 0px : (parent.width * mod(animation-tick(), 2s) / 1.5s);
|
|
y: (parent.height - self.height) / 2;
|
|
width: !root.indeterminate ? parent.width * root.prog : parent.width * 0.3;
|
|
border-radius: container.border-radius;
|
|
}
|
|
}
|
|
}
|
|
|
|
export component Slider {
|
|
in property<float> maximum: 100;
|
|
in property<float> minimum: 0;
|
|
in-out property<float> value;
|
|
out property<bool> has-focus: fs.has-focus;
|
|
in property<bool> enabled <=> touch.enabled;
|
|
callback changed(float);
|
|
|
|
min-height: 24px;
|
|
min-width: 100px;
|
|
horizontal-stretch: 1;
|
|
vertical-stretch: 0;
|
|
|
|
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;
|
|
|
|
|
|
Rectangle {
|
|
width: parent.width - parent.min-height;
|
|
x: parent.height / 2;
|
|
height: parent.min-height / 4;
|
|
y: (parent.height - self.height) / 2;
|
|
border-radius: self.height/2;
|
|
background: !root.enabled ? Palette.neutralLighter
|
|
: touch.has-hover ? Palette.themeLight
|
|
: Palette.neutralTertiaryAlt;
|
|
}
|
|
|
|
Rectangle {
|
|
width: (parent.width - parent.min-height) * ((root.value - root.minimum) / (root.maximum - root.minimum));
|
|
x: parent.height / 2;
|
|
height: parent.min-height / 4;
|
|
y: (parent.height - self.height) / 2;
|
|
border-radius: self.height/2;
|
|
background: !root.enabled ? Palette.neutralTertiary
|
|
: touch.has-hover ? Palette.themeSecondary
|
|
: Palette.neutralSecondary;
|
|
}
|
|
|
|
handle := Rectangle {
|
|
property<length> border: 3px;
|
|
width: self.height;
|
|
height: parent.height - 2 * self.border;
|
|
border-width: 3px;
|
|
border-radius: self.height / 2;
|
|
border-color: !root.enabled ? Palette.neutralTertiaryAlt
|
|
: touch.has-hover ? Palette.themePrimary
|
|
: Palette.neutralSecondary;
|
|
background: Palette.white;
|
|
x: (root.width - handle.width) * (root.value - root.minimum)/(root.maximum - root.minimum);
|
|
y: self.border;
|
|
}
|
|
touch := TouchArea {
|
|
width: parent.width;
|
|
height: parent.height;
|
|
property <float> pressed-value;
|
|
pointer-event(event) => {
|
|
if (event.button == PointerEventButton.left && event.kind == PointerEventKind.down) {
|
|
self.pressed-value = root.value;
|
|
}
|
|
}
|
|
moved => {
|
|
if (self.enabled && self.pressed) {
|
|
root.value = max(root.minimum, min(root.maximum,
|
|
self.pressed-value + (touch.mouse-x - touch.pressed-x) * (root.maximum - root.minimum) / (root.width - handle.width)));
|
|
root.changed(root.value);
|
|
}
|
|
}
|
|
}
|
|
|
|
fs := FocusScope {
|
|
x:0;
|
|
width: 0px;
|
|
|
|
key-pressed(event) => {
|
|
if (self.enabled && event.text == Key.RightArrow) {
|
|
root.value = Math.min(root.value + 1, root.maximum);
|
|
accept
|
|
} else if (self.enabled && event.text == Key.LeftArrow) {
|
|
root.value = Math.max(root.value - 1, root.minimum);
|
|
accept
|
|
} else {
|
|
reject
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle { // Focus rectangle
|
|
border-width: root.enabled && root.has-focus ? 1px : 0px;
|
|
border-color: Palette.black;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
export component GroupBox {
|
|
in property <string> title <=> label.text;
|
|
in property<bool> enabled: true;
|
|
|
|
VerticalLayout {
|
|
spacing: 8px;
|
|
padding-top: 16px;
|
|
padding-bottom: 8px;
|
|
|
|
label := Text {
|
|
vertical-stretch: 0;
|
|
color: !root.enabled ? Palette.neutralTertiary : Palette.neutralDark;
|
|
font-weight: 600;
|
|
}
|
|
Rectangle {
|
|
vertical-stretch: 1;
|
|
GridLayout {
|
|
@children
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export component TabWidgetImpl inherits Rectangle {
|
|
out property <length> content-x: 0;
|
|
out property <length> content-y: root.tabbar-preferred-height;
|
|
out property <length> content-height: root.height - root.tabbar-preferred-height;
|
|
out property <length> content-width: root.width;
|
|
out property <length> tabbar-x: 0;
|
|
out property <length> tabbar-y: 0;
|
|
out property <length> tabbar-height: root.tabbar-preferred-height;
|
|
out property <length> tabbar-width: root.width;
|
|
|
|
in property <length> tabbar-preferred-height;
|
|
in property <length> tabbar-preferred-width;
|
|
in property <length> content-min-height;
|
|
in property <length> content-min-width;
|
|
in property <int> current-index;
|
|
in property <int> current-focused;
|
|
|
|
preferred-width: root.content-min-width;
|
|
min-width: max(root.content-min-width, root.tabbar-preferred-width);
|
|
preferred-height: root.content-min-height + root.tabbar-preferred-height;
|
|
min-height: root.content-min-height + root.tabbar-preferred-height;
|
|
}
|
|
|
|
export component TabImpl inherits Rectangle {
|
|
in property<string> title <=> t.text;
|
|
in property<bool> enabled: true;
|
|
property<bool> has-focus: root.current-focused == root.tab-index;
|
|
in-out property<int> current; // The currently selected tab
|
|
in property<int> current-focused; // The currently focused tab
|
|
in property<int> tab-index; // The index of this tab
|
|
in property<int> num-tabs; // The total number of tabs
|
|
|
|
min-height: t.preferred-height + 16px;
|
|
preferred-width: t.preferred-width + 16px;
|
|
|
|
background: !root.enabled ? Palette.neutralLighter
|
|
: touch.pressed ? Palette.neutralLight
|
|
: touch.has-hover ? Palette.neutralLighter
|
|
: Palette.white;
|
|
horizontal-stretch: 0;
|
|
vertical-stretch: 0;
|
|
|
|
accessible-role: tab;
|
|
accessible-label: root.title;
|
|
|
|
touch := TouchArea {
|
|
enabled <=> root.enabled;
|
|
clicked => {
|
|
root.current = root.tab-index;
|
|
}
|
|
}
|
|
t := Text {
|
|
width: parent.width;
|
|
height: parent.height;
|
|
vertical-alignment: center;
|
|
horizontal-alignment: center;
|
|
color: !root.enabled ? Palette.neutralTertiary : Palette.neutralPrimary;
|
|
font-weight: root.current == root.tab-index ? 600 : 500;
|
|
}
|
|
|
|
Rectangle {
|
|
height: 3px;
|
|
width: touch.has-hover && root.current == root.tab-index ? parent.width : parent.width - 16px;
|
|
animate width { duration: 250ms; easing: ease-out; }
|
|
background: root.current == root.tab-index ? Palette.themeSecondary : transparent;
|
|
y: parent.height - self.height;
|
|
x: (parent.width - self.width) / 2;
|
|
}
|
|
|
|
Rectangle { // Focus Rectangle
|
|
border-width: root.enabled && root.has-focus ? 1px : 0px;
|
|
border-color: Palette.black;
|
|
}
|
|
}
|
|
|
|
export component TabBarImpl {
|
|
// injected properties:
|
|
in-out property<int> current; // The currently selected tab
|
|
in-out property<int> current-focused: fs.has-focus ? fs.focused-tab : -1; // The currently focused tab
|
|
in-out property<int> num-tabs; // The total number of tabs
|
|
|
|
HorizontalLayout {
|
|
spacing: 8px;
|
|
alignment: start;
|
|
@children
|
|
}
|
|
|
|
accessible-role: tab;
|
|
accessible-delegate-focus: root.current-focused >= 0 ? root.current-focused : root.current;
|
|
|
|
fs := FocusScope {
|
|
x:0;
|
|
width: 0px; // Do not react on clicks
|
|
property<int> focused-tab: 0;
|
|
|
|
key-pressed(event) => {
|
|
if (event.text == "\n") {
|
|
root.current = root.current-focused;
|
|
return accept;
|
|
}
|
|
if (event.text == Key.LeftArrow) {
|
|
self.focused-tab = Math.max(self.focused-tab - 1, 0);
|
|
return accept;
|
|
}
|
|
if (event.text == Key.RightArrow) {
|
|
self.focused-tab = Math.min(self.focused-tab + 1, root.num-tabs - 1);
|
|
return accept;
|
|
}
|
|
return reject;
|
|
}
|
|
|
|
key-released(event) => {
|
|
if (event.text == " ") {
|
|
root.current = root.current-focused;
|
|
return accept;
|
|
}
|
|
return reject;
|
|
}
|
|
}
|
|
}
|
|
|
|
export component TabWidget inherits TabWidget {}
|
|
|
|
export component LineEdit {
|
|
in property <length> font-size <=> inner.font-size;
|
|
in-out property <string> text <=> inner.text;
|
|
in property <string> placeholder-text <=> inner.placeholder-text;
|
|
out property <bool> has-focus: inner.has-focus;
|
|
in property <bool> enabled <=> inner.enabled;
|
|
in property input-type <=> inner.input-type;
|
|
in property horizontal-alignment <=> inner.horizontal-alignment;
|
|
in property read-only <=> inner.read-only;
|
|
callback accepted <=> inner.accepted;
|
|
callback edited <=> inner.edited;
|
|
forward-focus: inner;
|
|
// border-color: root.has-focus ? Palette.highlight-background : #ffffff;
|
|
|
|
public function select-all() {
|
|
inner.select-all();
|
|
}
|
|
public function cut() {
|
|
inner.cut();
|
|
}
|
|
public function copy() {
|
|
inner.copy();
|
|
}
|
|
public function paste() {
|
|
inner.paste();
|
|
}
|
|
|
|
horizontal-stretch: 1;
|
|
vertical-stretch: 0;
|
|
min-height: max(32px, l.min-height);
|
|
|
|
Rectangle {
|
|
background: !root.enabled ? Palette.neutralLighter : Palette.white;
|
|
border-radius: 2px;
|
|
border-width: !root.enabled ? 0px : root.has-focus ? 2px : 1px;
|
|
border-color: !root.enabled ? Palette.neutralLighter
|
|
: root.has-focus ? Palette.themeSecondary
|
|
: Palette.neutralPrimary;
|
|
}
|
|
|
|
l := HorizontalLayout {
|
|
padding-left: 8px;
|
|
padding-right: 8px;
|
|
padding-top: 3px;
|
|
padding-bottom: 3px;
|
|
inner := LineEditInner {
|
|
placeholder-color: !self.enabled ? Palette.neutralTertiary : Palette.neutralSecondary;
|
|
}
|
|
}
|
|
}
|
|
|
|
export component ListView inherits ScrollView {
|
|
@children
|
|
}
|
|
|
|
component StandardListViewBase inherits ListView {
|
|
private property <length> item-height: self.viewport-height / self.model.length;
|
|
private property <length> current-item-y: self.viewport-y + current-item * item-height;
|
|
|
|
in property<[StandardListViewItem]> model;
|
|
in-out property<int> current-item: -1;
|
|
|
|
for item[idx] in root.model : Rectangle {
|
|
l := HorizontalLayout {
|
|
padding: 8px;
|
|
spacing: 0px;
|
|
t := Text {
|
|
text: item.text;
|
|
color: Palette.neutralPrimary;
|
|
}
|
|
}
|
|
background: idx == root.current-item ? Palette.neutralLighter
|
|
: touch.has-hover ? Palette.neutralLighterAlt : transparent;
|
|
touch := TouchArea {
|
|
width: parent.width;
|
|
height: parent.height;
|
|
|
|
clicked => {
|
|
set-current-item(idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function set-current-item(index: int) {
|
|
if(index < 0 || index >= model.length) {
|
|
return;
|
|
}
|
|
|
|
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 {
|
|
in property <[string]> model;
|
|
in-out property <int> current-index : 0;
|
|
in-out property <string> current-value: root.model[root.current-index];
|
|
//property <bool> is-open: false;
|
|
callback selected(string);
|
|
|
|
accessible-role: combobox;
|
|
accessible-value <=> root.current-value;
|
|
|
|
out property has-focus <=> fs.has-focus;
|
|
in property enabled <=> fs.enabled;
|
|
forward-focus: fs;
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
background: !root.enabled ? Palette.neutralLighter : Palette.white;
|
|
border-radius: 2px;
|
|
border-width: !root.enabled ? 0px : root.has-focus ? 3px : 1px;
|
|
border-color: !root.enabled ? Palette.neutralLighter
|
|
: root.has-focus ? Palette.themeSecondary
|
|
: Palette.neutralPrimary;
|
|
}
|
|
|
|
|
|
horizontal-stretch: 1;
|
|
vertical-stretch: 0;
|
|
min-width: 170px;
|
|
min-height: max(32px, l.min-height);
|
|
|
|
l := HorizontalLayout {
|
|
padding-left: 8px;
|
|
padding-right: 8px;
|
|
padding-bottom: 3px;
|
|
padding-top: 3px;
|
|
spacing: 8px;
|
|
t := Text {
|
|
text <=> root.current-value;
|
|
horizontal-alignment: left;
|
|
vertical-alignment: center;
|
|
horizontal-stretch: 1;
|
|
color: !root.enabled ? Palette.neutralTertiary
|
|
: root.has-focus || touch.has-hover ? Palette.neutralPrimary
|
|
: Palette.neutralSecondary;
|
|
min-width: 0;
|
|
}
|
|
Rectangle {
|
|
width: 25px;
|
|
Path {
|
|
x: (parent.width - self.width) / 2;
|
|
y: (parent.height - self.height) / 2;
|
|
height: 8px;
|
|
width: 25px;
|
|
commands: "M.22.4.5.64.78.4.74.36.5.6.26.36z";
|
|
fill: t.color;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
popup := PopupWindow {
|
|
x:0;
|
|
y: root.height;
|
|
width: root.width;
|
|
Rectangle {
|
|
border-color: Palette.neutralLighter;
|
|
border-width: 1px;
|
|
/*drop-shadow-color: Palette.neutralTertiary;
|
|
drop-shadow-blur: 5px;*/
|
|
background: Palette.white;
|
|
}
|
|
VerticalLayout {
|
|
for value[idx] in root.model: Rectangle {
|
|
background: idx == root.current-index ? Palette.neutralLighter
|
|
: item-area.has-hover ? Palette.neutralLighterAlt : transparent;
|
|
VerticalLayout {
|
|
padding: 10px;
|
|
Text {
|
|
text: value;
|
|
}
|
|
}
|
|
item-area := TouchArea {
|
|
width: 100%;
|
|
height: 100%;
|
|
clicked => {
|
|
if (root.enabled) {
|
|
root.current-index = idx;
|
|
root.current-value = value;
|
|
root.selected(root.current-value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export component VerticalBox inherits VerticalLayout {
|
|
spacing: StyleMetrics.layout-spacing;
|
|
padding: StyleMetrics.layout-padding;
|
|
}
|
|
export component HorizontalBox inherits HorizontalLayout {
|
|
spacing: StyleMetrics.layout-spacing;
|
|
padding: StyleMetrics.layout-padding;
|
|
}
|
|
export component GridBox inherits GridLayout {
|
|
spacing: StyleMetrics.layout-spacing;
|
|
padding: StyleMetrics.layout-padding;
|
|
}
|