Loading files
import {
IconButton,
LineEdit,
ScrollView,
} from "../../widgets.slint";
import {
Data,
PartAction,
PartData,
} from "../../api.slint";
component HorizontalLine inherits Rectangle {
height: 1px;
background: Data.theme.control-border-disabled;
}
component VerticalLine inherits Rectangle {
width: 1px;
background: Data.theme.control-border-disabled;
}
component PartListViewTextCell inherits Rectangle {
in property <string> text;
in property <string> font-family;
in property <length> font-size: 12px;
in property <TextHorizontalAlignment> horizontal-alignment: left;
preferred-width: txt.preferred-width + 10px;
background: Data.theme.window;
txt := Text {
x: 5px;
width: parent.width - 10px;
horizontal-alignment: horizontal-alignment;
vertical-alignment: center;
color: Data.theme.control-text;
font-family: font-family;
font-size: font-size;
text: text;
accessible-role: none;
}
}
export component PartListView inherits ScrollView {
in-out property <[PartData]> model; // Last item is "new part" row!
in property <bool> read-only;
property <length> row-height: 25px;
callback edited;
callback request-attributes-editor(part-index: int);
min-height: min(l.preferred-height, 3 * row-height);
min-width: l.min-width;
preferred-width: l.preferred-width;
preferred-height: l.preferred-height;
mouse-drag-pan-enabled: true;
pure function is-last-row(idx: int) -> bool {
idx == (model.length - 1)
}
pure function is-visible-row(idx: int) -> bool {
(!read-only) || (idx < (model.length - 1))
}
l := HorizontalLayout {
padding-right: 8px; // Reserve space for the scrollbar
VerticalLine { }
VerticalLayout {
HorizontalLine { }
mpn-txt := PartListViewTextCell {
height: row-height;
text: @tr("Part Number (MPN)");
}
HorizontalLine { }
for item[index] in model: VerticalLayout {
HorizontalLayout {
height: is-visible-row(index) ? row-height : 0;
visible: self.height > 0;
Rectangle {
width: row-height;
height: row-height;
background: Data.theme.control;
Image {
width: 70%;
height: self.width;
image-fit: contain;
colorize: Data.theme.control-text;
source: @image-url("../../../../bootstrap-icons/icons/cart3.svg");
accessible-role: none;
}
}
mpn-edt := LineEdit {
height: row-height;
text: item.mpn;
has-error: self.text.is-empty && (!is-last-row(index));
placeholder-text: is-last-row(index) ? @tr("Type to add a new part...") : "";
show-clear-button: is-last-row(index); // Empty MPN is not valid
read-only: read-only;
frameless: true;
accessible-label: mpn-txt.text;
text-edited(text) => {
// Ignore intermediate edits
}
text-accepted(text) => {
model[index].mpn = text;
edited();
}
clear-clicked => {
model[index].mpn = "";
}
key-pressed(event) => {
if is-last-row(index) && (event.text == Key.Escape) && (!self.text.is-empty) {
model[index].mpn = "";
return accept;
}
reject
}
}
}
HorizontalLine {
visible: is-visible-row(index);
}
}
}
VerticalLine { }
VerticalLayout {
HorizontalLine { }
manufacturer-txt := PartListViewTextCell {
height: row-height;
text: @tr("Manufacturer");
}
HorizontalLine { }
for item[index] in model: VerticalLayout {
manufacturer-edt := LineEdit {
height: is-visible-row(index) ? row-height : 0;
text: item.manufacturer;
has-error: self.text.is-empty && ((!is-last-row(index)) || (!item.mpn.is-empty));
show-clear-button: is-last-row(index); // Empty manufacturer is not valid
read-only: read-only;
frameless: true;
visible: self.height > 0;
accessible-label: manufacturer-txt.text;
text-edited(text) => {
// Ignore intermediate edits
}
text-accepted(text) => {
model[index].manufacturer = text;
edited();
}
clear-clicked => {
model[index].manufacturer = "";
}
key-pressed(event) => {
if is-last-row(index) && (event.text == Key.Escape) && (!self.text.is-empty) {
model[index].manufacturer = "";
return accept;
}
reject
}
}
HorizontalLine {
visible: is-visible-row(index);
}
}
}
VerticalLine { }
VerticalLayout {
HorizontalLine { }
attributes-txt := PartListViewTextCell {
height: row-height;
text: @tr("Attributes");
}
HorizontalLine { }
for item[index] in model: VerticalLayout {
attributes-ta := TouchArea {
height: is-visible-row(index) ? row-height : 0;
visible: self.height > 0;
mouse-cursor: pointer;
Rectangle {
background: attributes-ta.has-hover ? Data.theme.control-hovered : Data.theme.control;
clip: true;
HorizontalLayout {
padding: 5px;
for attribute[attr-index] in item.attributes: Text {
property <string> unit: Data.attribute-units[attribute.type][attribute.unit];
property <string> separator: (attr-index < (item.attributes.length - 2)) ? ", " : "";
vertical-alignment: center;
color: read-only ? Data.theme.control-text-muted : Data.theme.control-text;
text: (attribute.value.is-empty ? attribute.key : (attribute.value + unit)) + separator;
accessible-role: none;
}
Rectangle { }
Rectangle {
width: self.height;
Image {
width: 70%;
height: 70%;
image-fit: contain;
colorize: Data.theme.control-text.with-alpha(0.7);
source: @image-url("../../../../font-awesome/svgs/solid/arrow-up-right-from-square.svg");
accessible-role: none;
}
}
}
}
clicked => {
request-attributes-editor(index);
}
}
HorizontalLine {
visible: is-visible-row(index);
}
}
}
VerticalLine { }
if (!read-only) && (model.length > 2): VerticalLayout {
HorizontalLine { }
PartListViewTextCell {
height: row-height;
}
HorizontalLine { }
for item[index] in model: VerticalLayout {
move-up-btn := IconButton {
height: row-height;
background-color: Data.theme.control;
icon-size: 14px;
icon: @image-url("../../../../font-awesome/svgs/solid/arrow-up.svg");
enabled: (index > 0) && (!is-last-row(index));
accessible-label: "move up";
clicked => {
model[index].action = PartAction.move-up;
}
}
HorizontalLine { }
}
}
if (!read-only) && (model.length > 2): VerticalLine { }
if !read-only: VerticalLayout {
HorizontalLine { }
PartListViewTextCell {
height: row-height;
}
HorizontalLine { }
for item[index] in model: VerticalLayout {
duplicate-btn := IconButton {
height: row-height;
background-color: Data.theme.control;
icon-size: 14px;
icon: @image-url("../../../../font-awesome/svgs/solid/copy.svg");
enabled: !is-last-row(index);
accessible-label: "duplicate";
clicked => {
model[index].action = PartAction.duplicate;
}
}
HorizontalLine { }
}
}
if !read-only: VerticalLine { }
if !read-only: VerticalLayout {
HorizontalLine { }
PartListViewTextCell {
height: row-height;
}
HorizontalLine { }
for item[index] in model: VerticalLayout {
add-delete-btn := IconButton {
height: row-height;
background-color: Data.theme.control;
icon-size: 13px;
icon: {
if is-last-row(index) {
@image-url("../../../../font-awesome/svgs/solid/check.svg")
} else {
@image-url("../../../../font-awesome/svgs/solid/trash-can.svg")
}
};
enabled: is-last-row(index) ? ((!item.mpn.is-empty) && (!item.manufacturer.is-empty)) : true;
accessible-label: is-last-row(index) ? "add" : "delete";
clicked => {
if is-last-row(index) {
edited();
} else {
model[index].action = PartAction.delete;
}
}
}
HorizontalLine { }
}
}
if !read-only: VerticalLine { }
}
}
c730ec352This file is pinned to the selected release. Download the original to open it in your design tool.