Loading files
import { ToolTip } from "widgets.slint";
import {
Action,
Backend,
Board2dTabData,
Data,
EditorCommandSet as Cmd,
Helpers,
PanelPage,
ProjectAction,
RuleCheckData,
RuleCheckState,
RuleCheckType,
TabType,
} from "api.slint";
enum Emblem {
none,
ok,
approved,
warning,
running,
}
component SideBarButton inherits Rectangle {
in property <image> icon;
in property <float> icon-scale: 1.0;
in property <string> tooltip;
in property <string> shortcut;
in property <Emblem> emblem: Emblem.none;
in property <string> status;
in property <color> status-color;
in property <color> status-background;
in property <bool> enabled: true;
in property <bool> checked: false;
callback clicked <=> ta.clicked;
background: {
if self.checked || ta.pressed {
Data.theme.control-checked
} else if ta.has-hover {
Data.theme.control-hovered
} else {
Data.theme.window // Not transparent because of the emblem color.
}
};
height: self.width;
// Accessibility
accessible-role: button;
accessible-enabled: enabled;
accessible-label: tooltip;
accessible-value: status;
accessible-action-default => {
ta.clicked();
}
if checked: Rectangle {
x: 0px;
width: 3px;
background: Data.theme.accent;
}
ta := TouchArea {
enabled: enabled;
}
Image {
width: parent.width * icon-scale * 0.7;
height: self.width;
colorize: enabled ? Data.theme.control-text : Data.theme.control-text-disabled;
image-fit: contain;
source: icon;
accessible-role: none;
}
if (emblem == Emblem.ok) || emblem == Emblem.approved: Rectangle {
x: parent.width - self.width - 3px;
y: parent.height - self.height - 3px;
width: 10px;
height: self.width;
background: root.background;
border-radius: self.height / 2;
Image {
width: 100%;
height: 100%;
image-fit: contain;
colorize: {
if emblem == Emblem.ok {
Data.theme.base-text-success
} else {
Data.theme.base-text-info
}
};
source: @image-url("../../font-awesome/svgs/solid/check.svg");
}
}
if emblem == Emblem.warning: Rectangle {
x: parent.width - self.width - 1px;
y: parent.height - self.height - 1px;
width: 40%;
height: self.width;
Image {
width: 100%;
height: 100%;
image-fit: contain;
source: @image-url("../../../img/status/dialog_warning.png");
}
}
if (emblem == Emblem.running) || ((emblem == Emblem.none) && (!status.is-empty)): Rectangle {
x: parent.width - self.width - 2px;
y: parent.height - self.height - 2px;
width: (emblem == Emblem.running) ? self.height : max(status-txt.preferred-width + 8px, self.height);
height: status-txt.preferred-height + 2px;
border-radius: self.height / 2;
background: (emblem == Emblem.running) ? Data.theme.warning : status-background;
status-txt := Text {
function get-progress-dots() -> string {
if (mod(animation-tick(), 1000ms) >= 666ms) {
return "•••";
} else if (mod(animation-tick(), 1000ms) >= 333ms) {
return "••";
} else {
return "•";
}
}
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2 + 1px;
font-size: (root.width < 40px) ? 8px : 10px;
font-weight: 700;
color: (emblem == Emblem.running) ? Data.theme.warning-text : status-color;
text: (emblem == Emblem.running) ? get-progress-dots() : status;
accessible-role: none;
}
}
if ta.has-hover && (!ta.pressed): tt := ToolTip {
x: root.width + 5px;
y: (root.height / 2) - (self.height / 2);
text: tooltip;
shortcut: shortcut;
}
}
export component SideBar {
function button-clicked(page: PanelPage) {
if Data.panel-page == page {
Data.panel-page = PanelPage.none;
} else {
Data.panel-page = page;
}
}
VerticalLayout {
spacing: 0px;
home-btn := SideBarButton {
icon: @image-url("../../bootstrap-icons/icons/house-fill.svg");
tooltip: @tr("Home");
checked: Data.panel-page == PanelPage.home;
clicked => {
button-clicked(PanelPage.home);
}
}
documents-btn := SideBarButton {
icon: @image-url("../../bootstrap-icons/icons/files.svg");
tooltip: @tr("Open Documents");
enabled: (Data.projects.length + Data.libraries.length) > 0;
checked: Data.panel-page == PanelPage.documents;
clicked => {
button-clicked(PanelPage.documents);
}
changed enabled => {
if self.checked && (!self.enabled) {
Data.panel-page = PanelPage.home;
}
}
}
layers-btn := SideBarButton {
icon: Cmd.dock-layers.icon;
tooltip: @tr("Layers");
shortcut: Cmd.dock-layers.shortcut;
enabled: Data.current-tab.layers.length > 0;
checked: Data.panel-page == PanelPage.layers;
clicked => {
button-clicked(PanelPage.layers);
}
changed enabled => {
if self.checked && (!self.enabled) {
if documents-btn.enabled {
Data.panel-page = PanelPage.documents;
} else {
Data.panel-page = PanelPage.home;
}
}
}
}
place-devices-btn := SideBarButton {
property <Board2dTabData> tab: Data.current-board-2d-tab;
icon: Cmd.dock-place-devices.icon;
tooltip: @tr("Place Devices");
shortcut: Cmd.dock-place-devices.shortcut;
enabled: Data.current-tab.type == TabType.board-2d;
checked: Data.panel-page == PanelPage.place-devices;
states [
disabled when !self.enabled: {
emblem: Emblem.none;
status: "";
}
unplaced-components when tab.unplaced-components.length > 0: {
emblem: Emblem.none;
status: tab.unplaced-components.length;
status-background: Data.theme.warning;
status-color: Data.theme.warning-text;
}
up-to-date when tab.unplaced-components.length == 0: {
emblem: Emblem.ok;
}
]
clicked => {
button-clicked(PanelPage.place-devices);
}
changed enabled => {
if self.checked && (!self.enabled) {
if documents-btn.enabled {
Data.panel-page = PanelPage.documents;
} else {
Data.panel-page = PanelPage.home;
}
}
}
}
rule-check-btn := SideBarButton {
property <RuleCheckData> check: Data.current-rule-check;
property <string> title: Helpers.get-rule-check-title-for-type(check.type);
icon: @image-url("../../bootstrap-icons/icons/list-check.svg");
tooltip: title;
shortcut: Helpers.get-rule-check-shortcut-for-type(check.type);
enabled: check.type != RuleCheckType.none;
checked: Data.panel-page == PanelPage.rule-check;
states [
not-available when check.type == RuleCheckType.none: {
emblem: Emblem.none;
status: "";
}
running when check.state == RuleCheckState.running: {
emblem: Emblem.running;
}
failed when !check.execution-error.is-empty: {
emblem: Emblem.warning;
}
not-run-yet when check.state == RuleCheckState.not-run-yet: {
emblem: Emblem.none;
status: "?";
status-background: Data.theme.base.mix(Data.theme.base-text, 0.65);
status-color: Data.theme.base-text;
}
outdated when check.state == RuleCheckState.outdated: {
emblem: Emblem.none;
status: check.unapproved;
status-background: Data.theme.base.mix(Data.theme.base-text, 0.65);
status-color: Data.theme.base-text;
}
up-to-date-error when (check.state == RuleCheckState.up-to-date) && (check.unapproved != 0): {
emblem: Emblem.none;
status: check.unapproved;
status-background: Data.theme.error;
status-color: Data.theme.error-text;
}
up-to-date-approved when (check.state == RuleCheckState.up-to-date) && (check.messages.length != 0): {
emblem: Emblem.approved;
}
up-to-date-success when (check.state == RuleCheckState.up-to-date) && (check.messages.length == 0): {
emblem: Emblem.ok;
}
]
clicked => {
button-clicked(PanelPage.rule-check);
}
changed enabled => {
if self.checked && (!self.enabled) {
if documents-btn.enabled {
Data.panel-page = PanelPage.documents;
} else {
Data.panel-page = PanelPage.home;
}
}
}
}
output-jobs-btn := SideBarButton {
icon: @image-url("../../bootstrap-icons/icons/play.svg");
tooltip: Cmd.output-jobs.text;
shortcut: Cmd.output-jobs.shortcut;
enabled: Data.current-project.valid;
clicked => {
Helpers.trigger-current-project-action(ProjectAction.open-output-jobs);
}
}
order-pcb-btn := SideBarButton {
icon: @image-url("../../bootstrap-icons/icons/cart3.svg");
tooltip: Cmd.order-pcb.text;
shortcut: Cmd.order-pcb.shortcut;
enabled: Data.current-project.valid;
checked: Data.panel-page == PanelPage.order;
clicked => {
button-clicked(PanelPage.order);
}
changed enabled => {
if self.checked && (!self.enabled) {
Data.panel-page = PanelPage.home;
}
}
}
Rectangle {
vertical-stretch: 1;
}
libraries-btn := SideBarButton {
icon: @image-url("../../bootstrap-icons/icons/database.svg");
icon-scale: 1.2;
tooltip: @tr("Libraries");
shortcut: Cmd.library-manager.shortcut;
checked: Data.panel-page == PanelPage.libraries;
states [
// Note: Do not display the "refreshing" state as it is usually
// very short would would look like a flicker.
refreshing when Data.local-libraries-data.refreshing || Data.remote-libraries-data.refreshing: {
// Just keep the previous state.
}
busy when Data.libraries-operation-in-progress: {
emblem: Emblem.running;
}
local-error when !Data.local-libraries-data.refreshing-error.is-empty: {
emblem: Emblem.warning;
}
remote-error when !Data.remote-libraries-data.refreshing-error.is-empty: {
emblem: Emblem.warning;
}
outdated when Data.remote-libraries-data.outdated > 0: {
emblem: Emblem.none;
status: Data.remote-libraries-data.outdated;
status-background: Data.theme.info;
status-color: Data.theme.info-text;
}
no-libraries when (Data.local-libraries-data.count == 0) && (Data.remote-libraries-data.installed == 0): {
emblem: Emblem.none;
status: "0";
status-background: Data.theme.error;
status-color: Data.theme.error-text;
}
up-to-date when Data.remote-libraries-data.all-up-to-date: {
emblem: Emblem.ok;
}
]
clicked => {
button-clicked(PanelPage.libraries);
}
}
workspace-settings-btn := SideBarButton {
icon: @image-url("../../bootstrap-icons/icons/sliders.svg");
tooltip: Cmd.workspace-settings.text;
shortcut: Cmd.workspace-settings.shortcut;
checked: false;
clicked => {
Backend.trigger(Action.workspace-settings);
}
}
about-btn := SideBarButton {
icon: @image-url("../../bootstrap-icons/icons/question-circle.svg");
tooltip: @tr("Help / About");
shortcut: Cmd.about-librepcb.shortcut;
checked: Data.panel-page == PanelPage.about;
clicked => {
button-clicked(PanelPage.about);
}
}
}
}
94ec4b8feThis file is pinned to the selected release. Download the original to open it in your design tool.