Loading files
import { ComponentSignalListView } from "componentsignallistview.slint";
import { ComponentVariantListView } from "componentvariantlistview.slint";
import { Tab } from "../../tab.slint";
import {
AttributesRowHeader,
AuthorRowHeader,
CategoriesChooserDialog,
CategoriesRowCell,
CategoriesRowHeader,
DatasheetRowCell,
DatasheetRowHeader,
DefaultValueRowHeader,
DeprecatedRowCell,
DeprecatedRowHeader,
DescriptionRowHeader,
KeywordsRowHeader,
MetadataFooterRowCell,
NameRowHeader,
PrefixRowHeader,
SchematicOnlyRowHeader,
VersionRowHeader,
} from "../metadatawidgets.slint";
import {
BreakingChangesMessageBanner,
CheckErrorsMessageBanner,
ElementDuplicatedMessageBanner,
} from "../messagebanners.slint";
import {
WizardHeaderPanel,
WizardHeaderText,
} from "../wizardheader.slint";
import {
AttributeListView,
Button,
CheckBox,
IconButton,
LineEdit,
LinkText,
NavBar,
TextEdit,
} from "../../widgets.slint";
import {
Backend,
ComponentTabData,
Data,
FeatureState,
TabAction,
WindowSectionData,
} from "../../api.slint";
component ComponentMetadataTab inherits GridLayout {
in property <int> section-index;
in-out property <[ComponentTabData]> tabs;
in property <int> index;
in property <bool> read-only;
in property <length> window-height;
padding: 8px;
spacing: 8px;
Row {
name-txt := NameRowHeader { }
name-edt := LineEdit {
text: tabs[index].name;
validation-error: tabs[index].name-error;
read-only: read-only;
accessible-label: name-txt.text;
init => {
if (!read-only) && tabs[index].wizard-mode {
self.select-all();
}
}
text-edited(text) => {
tabs[index].name = text;
}
text-accepted(text) => {
tabs[index].name = text;
Backend.trigger-tab(section-index, index, TabAction.apply);
}
}
}
Row {
description-txt := DescriptionRowHeader { }
description-edt := TextEdit {
min-height: 65px; // Corresponds to 3 text rows
max-height: 250px; // Allow growing up to this height for long texts
text: tabs[index].description;
read-only: read-only;
accessible-label: description-txt.text;
text-edited(text) => {
tabs[index].description = text;
}
text-accepted(text) => {
tabs[index].description = text;
Backend.trigger-tab(section-index, index, TabAction.apply);
}
}
}
Row {
keywords-txt := KeywordsRowHeader { }
keywords-edt := LineEdit {
text: tabs[index].keywords;
read-only: read-only;
accessible-label: keywords-txt.text;
text-edited(text) => {
tabs[index].keywords = text;
}
text-accepted(text) => {
tabs[index].keywords = text;
Backend.trigger-tab(section-index, index, TabAction.apply);
}
}
}
Row {
author-txt := AuthorRowHeader { }
author-edt := LineEdit {
text: tabs[index].author;
read-only: read-only;
accessible-label: author-txt.text;
text-edited(text) => {
tabs[index].author = text;
}
text-accepted(text) => {
tabs[index].author = text;
Backend.trigger-tab(section-index, index, TabAction.apply);
}
}
}
Row {
version-txt := VersionRowHeader { }
version-edt := LineEdit {
text: tabs[index].version;
validation-error: tabs[index].version-error;
read-only: read-only;
accessible-label: version-txt.text;
text-edited(text) => {
tabs[index].version = text;
}
text-accepted(text) => {
tabs[index].version = text;
Backend.trigger-tab(section-index, index, TabAction.apply);
}
}
}
Row {
deprecated-txt := DeprecatedRowHeader { }
deprecated-sw := DeprecatedRowCell {
checked: tabs[index].deprecated;
read-only: read-only;
accessible-label: deprecated-txt.text;
toggled(checked) => {
tabs[index].deprecated = checked;
Backend.trigger-tab(section-index, index, TabAction.apply);
}
}
}
Row {
categories-txt := CategoriesRowHeader { }
categories-cell := CategoriesRowCell {
model: tabs[index].categories;
read-only: read-only;
accessible-label: categories-txt.text;
add-triggered => {
tabs[index].choose-category = true;
}
}
}
Row {
datasheet-txt := DatasheetRowHeader { }
datasheet-cell := DatasheetRowCell {
url: tabs[index].datasheet-url;
url-error: tabs[index].datasheet-url-error;
read-only: read-only;
label: datasheet-txt.text;
text-edited(text) => {
tabs[index].datasheet-url = text;
}
text-accepted(text) => {
tabs[index].datasheet-url = text;
Backend.trigger-tab(section-index, index, TabAction.apply);
}
open-url-clicked(url) => {
Backend.trigger-tab(section-index, index, TabAction.open-datasheet);
}
}
}
Row {
schematic-only-txt := SchematicOnlyRowHeader { }
schematic-only-sw := CheckBox {
font-size: 11px;
text: @tr("Check this if this component cannot be placed on boards (e.g. supply symbols, sheet frames, ...).");
checked: tabs[index].schematic-only;
enabled: !read-only;
accessible-label: schematic-only-txt.text;
accessible-description: self.text;
toggled(checked) => {
tabs[index].schematic-only = checked;
Backend.trigger-tab(section-index, index, TabAction.apply);
}
}
}
Row {
prefix-txt := PrefixRowHeader { }
prefix-edt := LineEdit {
text: tabs[index].prefix;
validation-error: tabs[index].prefix-error;
placeholder-text: @tr("E.g. \"{}\" for resistors, or \"{}\" for integrated circuits", "R", "U");
read-only: read-only;
accessible-label: prefix-txt.text;
text-edited(text) => {
tabs[index].prefix = text;
}
text-accepted(text) => {
tabs[index].prefix = text;
Backend.trigger-tab(section-index, index, TabAction.apply);
}
}
}
Row {
default-value-txt := DefaultValueRowHeader { }
default-value-edt := TextEdit {
min-height: default-value-hint-l.preferred-height;
text: tabs[index].default-value;
validation-error: tabs[index].default-value-error;
read-only: read-only;
accessible-label: default-value-txt.text;
accessible-placeholder-text: default-value-hint-txt.text;
default-value-hint-l := GridLayout {
padding: 8px;
spacing-horizontal: 8px;
spacing-vertical: 2px;
visible: tabs[index].default-value.is-empty;
Row {
default-value-hint-txt := Text {
colspan: 2;
color: Data.theme.control-text-muted;
font-size: 11px;
wrap: word-wrap;
text: @tr("The VALUE the component gets when added to the schematics.");
accessible-role: none;
}
}
Row {
specific-txt := Text {
horizontal-stretch: 0;
overflow: elide;
color: Data.theme.control-text-muted;
font-size: 11px;
text: @tr("For specific components (e.g. \"ESP32-S3R2\"):");
accessible-role: none;
}
specific-btn := LinkText {
horizontal-stretch: 1;
text-color: Data.theme.control-text-muted;
font-size: 11px;
text: "{{MPN or DEVICE or COMPONENT}}";
accessible-description: specific-txt.text;
clicked => {
tabs[index].default-value = self.text;
}
}
}
Row {
generic-txt := Text {
horizontal-stretch: 0;
overflow: elide;
color: Data.theme.control-text-muted;
font-size: 11px;
text: @tr("For generic components (e.g. \"OpAmp\"):");
accessible-role: none;
}
generic-btn := LinkText {
horizontal-stretch: 1;
text-color: Data.theme.control-text-muted;
font-size: 11px;
text: "{{MPN or DEVICE}}";
accessible-description: generic-txt.text;
clicked => {
tabs[index].default-value = self.text;
}
}
}
}
text-edited(text) => {
tabs[index].default-value = text;
}
text-accepted(text) => {
tabs[index].default-value = text;
Backend.trigger-tab(section-index, index, TabAction.apply);
}
}
}
Row {
attributes-txt := AttributesRowHeader { }
attributes-view := AttributeListView {
model: tabs[index].attributes;
read-only: read-only;
window-height: window-height;
edited => {
Backend.trigger-tab(section-index, index, TabAction.apply);
}
}
}
Row {
Rectangle { }
footer-cell := MetadataFooterRowCell {
section-index: section-index;
tab-index: index;
wizard-mode: tabs[index].wizard-mode;
unsaved-changes: Data.sections[section-index].tabs[index].unsaved-changes;
checks: tabs[index].checks;
read-only: read-only;
}
}
Row {
Rectangle { }
}
}
component ComponentAddGatesTab inherits VerticalLayout {
in property <int> section-index;
in-out property <WindowSectionData> section;
in-out property <[ComponentTabData]> tabs;
in property <int> index;
in property <bool> read-only;
in property <length> window-height;
alignment: start;
variants-view := ComponentVariantListView {
model: tabs[index].variants;
signal-names: tabs[index].signal-names;
unit: tabs[index].unit;
wizard-mode: true;
read-only: read-only;
window-height: window-height;
render-scene(variant, gate, width, height, frame-dummy) => {
Backend.render-scene(section-index, width, height, variant * 1000 + gate, frame-dummy)
}
add-variant-clicked => {
Backend.trigger-tab(section-index, index, TabAction.component-add-variant);
}
}
HorizontalLayout {
alignment: start;
padding: 8px;
spacing: 8px;
next-btn := Button {
width: max(self.preferred-width, 100px);
icon: @image-url("../../../../font-awesome/svgs/solid/angle-right.svg");
text: self.primary ? @tr("Next") : @tr("Skip");
primary: tabs[index].variants[0].gates.length > 0;
clicked => {
Backend.trigger-tab(section-index, index, TabAction.next);
}
}
back-btn := Button {
width: self.preferred-width;
icon: @image-url("../../../../font-awesome/svgs/solid/angle-left.svg");
text: @tr("Back");
clicked => {
Backend.trigger-tab(section-index, index, TabAction.back);
}
}
}
}
component ComponentSignalsEditorTab inherits VerticalLayout {
in property <int> section-index;
in-out property <WindowSectionData> section;
in-out property <[ComponentTabData]> tabs;
in property <int> index;
in property <bool> read-only;
alignment: start;
padding-left: 8px;
padding-top: 8px;
spacing: 10px;
callback focus-rejected <=> signals-view.focus-rejected;
if !read-only: WizardHeaderPanel {
if tabs[index].wizard-mode: WizardHeaderText {
property <string> s1: @tr("Now review & correct the list of signals, if needed (often it is not needed, then just proceed).");
property <string> s2: @tr("If you add or remove any signals, don't forget to update the pinout afterwards (on the next step)!");
text: s1 + " " + s2;
}
WizardHeaderText {
property <string> s1: @tr("This signal list should represent the *electrical* interface of the component, not the *mechanical* pads.");
property <string> s2: @tr("For example, if an IC has two or more GND pads which are internally connected, only one GND signal should be added here.");
property <string> s3: @tr("Note that once the component is finished, it is *not* possible anymore to add or remove signals.");
font-italic: true;
text: @tr("Important:") + " " + s1 + " " + s2 + " " + s3;
}
}
if !read-only: HorizontalLayout {
padding-right: 8px;
spacing: 5px;
new-signal-txt := Text {
vertical-alignment: center;
color: Data.theme.base-text;
text: @tr("Add Signals:");
accessible-role: none;
}
new-signal-edt := LineEdit {
placeholder-text: @tr("Signal name (may contain ranges like \"1..5\"; use prefix \"!\" for inverted signals)");
text: tabs[index].new-signal-name;
validation-error: tabs[index].new-signal-name-error;
accessible-label: new-signal-txt.text;
text-edited(text) => {
tabs[index].new-signal-name = text;
}
key-pressed(event) => {
if event.text == Key.Return {
new-signal-btn.clicked();
return accept;
}
reject
}
}
new-signal-btn := IconButton {
style: hyperlink;
icon-scale: 0.65;
icon: @image-url("../../../../font-awesome/svgs/solid/plus.svg");
enabled: !new-signal-edt.text.is-empty;
accessible-label: "add signal";
clicked => {
Backend.trigger-tab(section-index, index, TabAction.component-add-signals);
}
}
}
signals-view := ComponentSignalListView {
model: tabs[index].cmp-signals;
read-only: read-only;
edited => {
Backend.trigger-tab(section-index, index, TabAction.apply);
}
}
if tabs[index].wizard-mode: HorizontalLayout {
alignment: start;
padding: 8px;
spacing: 8px;
next-btn := Button {
width: max(self.preferred-width, 100px);
primary: true;
icon: @image-url("../../../../font-awesome/svgs/solid/check.svg");
text: @tr("Finish");
clicked => {
Backend.trigger-tab(section-index, index, TabAction.next);
}
}
back-btn := Button {
width: self.preferred-width;
icon: @image-url("../../../../font-awesome/svgs/solid/angle-left.svg");
text: @tr("Back");
clicked => {
Backend.trigger-tab(section-index, index, TabAction.back);
}
}
}
}
export component ComponentTab inherits Tab {
in property <int> section-index;
in-out property <WindowSectionData> section;
in property <length> window-height;
property <[ComponentTabData]> tabs: section.component-tabs;
property <int> index: section.current-tab-index;
property <bool> read-only: section.tabs[index].features.save != FeatureState.enabled;
enabled: true;
VerticalLayout {
BreakingChangesMessageBanner {
shown: tabs[index].interface-broken-msg;
is-unlocked: !read-only;
age-days: tabs[index].age-days;
action-invoked(action) => {
Backend.trigger-tab(section-index, index, action);
}
}
ElementDuplicatedMessageBanner {
shown: tabs[index].element-duplicated-msg;
deprecate-clicked => {
tabs[index].deprecated = true;
Backend.trigger-tab(section-index, index, TabAction.apply);
}
cancel-clicked => {
tabs[index].element-duplicated-msg = false;
}
}
CheckErrorsMessageBanner {
shown: tabs[index].checks.errors > 0;
}
// Pages in wizard mode:
// [0] Metadata tab: Set metadata
// [1] Add gates tab: Choose symbols
// [2] Signals tab: Modify signals
//
// Pages in normal mode:
// [0] Metadata tab
// [1] Signals tab
// [2] Variants & Gates tab
if !tabs[index].wizard-mode: navbar := NavBar {
pages: [
{ id: 0, title: @tr("Metadata") },
{ id: 1, title: @tr("Signals") },
{ id: 2, title: @tr("Variants & Gates") },
];
current-id: tabs[index].page-index;
page-clicked(id) => {
Data.set-current-tab(section-index, index);
tabs[index].page-index = id;
root.focus(); // The focused component may have been deleted.
}
}
if tabs[index].page-index <= 0: metadata-tab := ComponentMetadataTab {
section-index: section-index;
tabs: tabs;
index: index;
read-only: read-only;
window-height: window-height;
}
if tabs[index].wizard-mode && (tabs[index].page-index == 1): add-gates-tab := ComponentAddGatesTab {
section-index: section-index;
tabs: tabs;
index: index;
window-height: window-height;
}
if tabs[index].page-index == (tabs[index].wizard-mode ? 2 : 1): signals-tab := ComponentSignalsEditorTab {
section-index: section-index;
tabs: tabs;
index: index;
read-only: read-only;
focus-rejected => {
root.focus();
}
}
if tabs[index].page-index >= (tabs[index].wizard-mode ? 3 : 2): variants-tab := ComponentVariantListView {
model: tabs[index].variants;
signal-names: tabs[index].signal-names;
unit: tabs[index].unit;
wizard-mode: false;
read-only: read-only;
window-height: window-height;
render-scene(variant, gate, width, height, frame-dummy) => {
Backend.render-scene(section-index, width, height, variant * 1000 + gate, frame-dummy)
}
add-variant-clicked => {
Backend.trigger-tab(section-index, index, TabAction.component-add-variant);
}
}
}
if tabs[index].choose-category: add-category-dialog := CategoriesChooserDialog {
width: parent.width;
height: parent.height;
model: tabs[index].categories-tree;
item-picked(idx, item) => {
root.focus(); // Keep keyboard shortcuts working.
tabs[index].new-category = item.user-data;
// Close the dialog *as the last operation* to avoid crashes!
tabs[index].choose-category = false;
}
rejected => {
root.focus(); // Keep keyboard shortcuts working.
// Close the dialog *as the last operation* to avoid crashes!
tabs[index].choose-category = false;
}
}
focus-gained(reason) => {
Data.set-current-tab(section-index, index);
}
}
59219ad85This file is pinned to the selected release. Download the original to open it in your design tool.