Loading files
c730ec352This file is pinned to the selected release. Download the original to open it in your design tool.
import {
Button,
IconButton,
LengthEdit,
ListScrollView,
ListViewItemButton,
PanelHeader,
} from "widgets.slint";
import {
Backend,
BoardAction,
Data,
EditorCommandSet as Cmd,
FeatureState,
Helpers,
PackageTabData,
RuleCheckData,
RuleCheckMessageAction,
RuleCheckMessageData,
RuleCheckState,
RuleCheckType,
TabAction,
} from "api.slint";
component RuleCheckListItem inherits Rectangle {
in-out property <[RuleCheckMessageData]> model;
in property <int> index;
in property <bool> read-only: false;
in property <bool> enabled: true;
in property <bool> selected: false;
property <RuleCheckMessageData> item: model[index];
callback clicked <=> ta.clicked;
callback double-clicked <=> ta.double-clicked;
background: {
if self.selected {
Data.theme.control-checked
} else if ta.has-hover {
Data.theme.control-hovered
} else {
transparent
}
};
// Accessibility
accessible-role: list-item;
accessible-description: item.description;
accessible-enabled: enabled;
accessible-label: item.message;
accessible-item-selectable: true;
accessible-item-selected: selected;
accessible-item-index: index;
accessible-action-default => {
ta.double-clicked();
}
ta := TouchArea {
enabled: enabled;
HorizontalLayout {
padding: 3px;
padding-left: 5px;
padding-right: 5px;
spacing: 5px;
Rectangle {
width: self.height;
Image {
x: 2px;
y: self.x;
width: self.height;
height: parent.height - 2 * self.x;
source: Helpers.get-icon-for-notification-type(item.severity);
colorize: (item.approved || item.autofixed || (!root.enabled)) ? txt.color : Helpers.get-color-for-notification-type(item.severity);
accessible-role: none;
}
}
txt := Text {
vertical-alignment: center;
font-size: 12px;
font-italic: item.approved;
overflow: elide;
text: item.message;
color: (root.enabled && (!item.autofixed)) ? Data.theme.control-text : Data.theme.control-text-disabled;
accessible-role: none;
}
if (!read-only) && item.supports-autofix && (selected || ta.has-hover): autofix-btn := ListViewItemButton {
icon: @image-url("../../font-awesome/svgs/solid/wand-magic-sparkles.svg");
tooltip: @tr("Apply Automatic Fix");
enabled: root.enabled && (!item.autofixed) && (item.action != RuleCheckMessageAction.autofix);
clicked => {
model[index].action-window-id = Data.window-id;
model[index].action = RuleCheckMessageAction.autofix;
}
}
if (!read-only) && (selected || ta.has-hover || item.approved): approve-btn := ListViewItemButton {
icon: item.approved ? @image-url("../../bootstrap-icons/icons/eye-slash-fill.svg") : @image-url("../../bootstrap-icons/icons/eye-fill.svg");
tooltip: item.approved ? @tr("Remove Approval") : @tr("Approve");
enabled: root.enabled && (!item.autofixed);
clicked => {
model[index].approved = !item.approved;
}
}
}
}
// Strikethrough
if item.approved: Rectangle {
x: txt.x;
y: txt.y + (txt.height / 2) + 1px;
height: 1px;
width: min(txt.preferred-width, txt.width);
background: txt.color;
}
}
export component RuleCheckPanel inherits FocusScope {
property <RuleCheckData> check: Data.current-rule-check;
property <string> title: Helpers.get-rule-check-title-for-type(check.type);
property <[RuleCheckMessageData]> model: check.messages;
property <length> item-height: 22px;
property <int> selected-index: -1;
property <RuleCheckMessageData> selected-item: model[selected-index];
property <bool> item-selected: (selected-index >= 0) && (selected-index < model.length);
property <bool> zoom-to-location: {
if check.type == RuleCheckType.drc {
Data.drc-zoom-to-location
} else {
Data.erc-zoom-to-location
}
};
property <bool> settings-expanded: false;
function set-current-index(index: int, force-zoom: bool) {
if ((index + 1) * item-height) > (view.height - view.viewport-y) {
view.viewport-y = -(index + 1) * item-height + view.height;
} else if (index * item-height) < -(view.viewport-y) {
view.viewport-y = -(index * item-height);
}
selected-index = index;
model[index].action-window-id = Data.window-id;
if zoom-to-location || force-zoom {
model[index].action = RuleCheckMessageAction.highlight-and-zoom-to;
} else {
model[index].action = RuleCheckMessageAction.highlight;
}
}
// Set focus when panel is shown. Not sure if it's always desired(?).
init => {
root.focus();
}
VerticalLayout {
header := PanelHeader {
z: 10; // For the tooltip.
title: title.to-uppercase();
show-spinner: check.state == RuleCheckState.running;
highlight: root.has-focus;
if !check.execution-error.is-empty: error-btn := IconButton {
width: self.height;
style: read-only;
icon: @image-url("../../../img/status/dialog_error.png");
icon-scale: 0.7;
tooltip: check.execution-error;
colorize: false;
}
if check.state == RuleCheckState.outdated: outdated-btn := IconButton {
width: self.height;
style: read-only;
icon: @image-url("../../font-awesome/svgs/solid/triangle-exclamation.svg");
icon-scale: 0.7;
color-disabled: self.color-enabled.darker(1);
color-enabled: Data.theme.base-text-warning;
tooltip: @tr("Possibly Outdated Data") + "\n" + @tr("Trigger the checks again to refresh the messages.");
}
if check.type == RuleCheckType.drc: quick-check-btn := IconButton {
width: self.height;
cmd: Cmd.run-quick-check;
icon-scale: 0.7;
enabled: check.state != RuleCheckState.running;
clicked => {
Helpers.trigger-current-board-action(BoardAction.run-quick-check);
}
}
if check.type == RuleCheckType.drc: drc-btn := IconButton {
width: self.height;
cmd: Cmd.run-design-rule-check;
icon: @image-url("../../font-awesome/svgs/solid/arrows-rotate.svg");
icon-scale: 0.7;
enabled: check.state != RuleCheckState.running;
clicked => {
Helpers.trigger-current-board-action(BoardAction.run-drc);
}
}
if check.type == RuleCheckType.drc: drc-setup-btn := IconButton {
width: self.height;
icon: @image-url("../../font-awesome/svgs/solid/sliders.svg");
icon-scale: 0.7;
tooltip: @tr("DRC Settings");
enabled: check.state != RuleCheckState.running;
clicked => {
Helpers.trigger-current-board-action(BoardAction.open-drc-setup-dialog);
}
}
if check.type == RuleCheckType.package-check: settings-btn := IconButton {
width: self.height;
icon: @image-url("../../font-awesome/svgs/solid/sliders.svg");
icon-scale: 0.7;
tooltip: @tr("Settings");
enabled: check.state != RuleCheckState.running;
clicked => {
settings-expanded = !settings-expanded;
}
}
if (check.type == RuleCheckType.erc) || (check.type == RuleCheckType.drc): zoom-to-location-btn := IconButton {
width: self.height;
icon: @image-url("../../font-awesome/svgs/solid/location-crosshairs.svg");
icon-scale: 0.7;
tooltip: @tr("Zoom to Location");
checked: zoom-to-location;
enabled: check.state != RuleCheckState.running;
clicked => {
if check.type == RuleCheckType.drc {
Data.drc-zoom-to-location = !Data.drc-zoom-to-location;
} else {
Data.erc-zoom-to-location = !Data.erc-zoom-to-location;
}
}
}
}
if (check.type == RuleCheckType.package-check) && settings-expanded: Rectangle {
property <int> section-index: Data.current-section-index;
property <[PackageTabData]> tabs: Data.current-section.package-tabs;
property <int> index: Data.current-section.current-tab-index;
property <bool> read-only: Data.current-section.tabs[index].features.save != FeatureState.enabled;
Rectangle {
y: parent.height - self.height;
height: 1px;
background: Data.theme.control-border;
}
GridLayout {
padding: 5px;
spacing: 5px;
Row {
Text {
colspan: 2;
text: @tr("These settings are stored in the currently opened package and don't affect other packages.");
font-size: 11px;
font-italic: true;
color: Data.theme.control-text;
wrap: word-wrap;
}
}
Row {
min-copper-clearance-txt := Text {
width: self.preferred-width;
text: @tr("Min. Copper Clearance:");
vertical-alignment: center;
color: Data.theme.control-text;
accessible-role: none;
}
LengthEdit {
z: 100;
height: self.preferred-height;
data: tabs[index].min-copper-clearance;
enabled: !read-only;
tooltip: @tr("Recommended: {} (default)", "200μm") + "\n" + @tr("Reduce it for packages with very small pitch");
accessible-label: min-copper-clearance-txt.text;
value-changed(value) => {
tabs[index].min-copper-clearance.value = value;
}
unit-changed(unit) => {
tabs[index].min-copper-clearance.unit = unit;
}
increase-triggered() => {
tabs[index].min-copper-clearance.increase = true;
}
decrease-triggered() => {
tabs[index].min-copper-clearance.decrease = true;
}
}
}
Row {
settings-apply-btn := Button {
colspan: 2;
text: @tr("Apply");
primary: true;
clicked => {
Backend.trigger-tab(section-index, index, TabAction.apply);
}
}
}
}
}
if model.length == 0: VerticalLayout {
padding: 8px;
Text {
width: 100%;
font-size: 12px;
font-italic: true;
color: Data.theme.control-text-muted;
wrap: word-wrap;
text: {
if check.state == RuleCheckState.not-run-yet {
return @tr("No checks run yet. Trigger them with one of the buttons above.");
} else if check.state == RuleCheckState.running {
return @tr("Running, hang on...");
} else {
return @tr("No messages.");
}
}
}
}
view := ListScrollView {
vertical-stretch: 1;
preferred-height: model.length * item-height;
viewport-height: model.length * item-height;
visible: model.length > 0;
force-show-scrollbars: root.has-focus;
for msg[index] in model: item := RuleCheckListItem {
height: item-height;
model: model;
index: index;
read-only: check.read-only;
enabled: check.execution-error.is-empty && (check.state != RuleCheckState.running);
selected: (index == selected-index);
clicked => {
set-current-index(index, false);
root.focus();
}
double-clicked => {
set-current-index(index, true);
root.focus();
}
}
}
if item-selected: selected-msg-header := PanelHeader {
title: selected-item.message;
}
if item-selected: VerticalLayout {
padding: 8px;
selected-msg-txt := Text {
// Only the DRC is not run automatically, thus the
// autofix state persists until it is run again.
property <bool> autofixed: selected-item.autofixed && (check.type == RuleCheckType.drc);
width: 100%;
font-size: 12px;
font-italic: autofixed;
color: Data.theme.control-text;
wrap: word-wrap;
text: autofixed ? @tr("This message is marked as fixed. Run the checks again to verify the message is really resolved and disappears.") : selected-item.description;
}
}
}
key-pressed(event) => {
if (event.text == Key.DownArrow) && (selected-index < model.length - 1) {
set-current-index(selected-index + 1, false);
return accept;
} else if (event.text == Key.UpArrow) && (selected-index > 0) {
set-current-index(selected-index - 1, false);
return accept;
} else if item-selected {
if event.text == Key.Return {
model[selected-index].action = RuleCheckMessageAction.highlight-and-zoom-to;
return accept;
} else if event.text == Key.Space {
model[selected-index].approved = !model[selected-index].approved;
return accept;
}
}
reject
}
}