import { ScrollView, TextEdit } from "std-widgets.slint"; import { SettingsDialog, SettingsButton } from "settings_controls.slint"; import { WorkbenchStyle } from "workbench_controls.slint"; import { UiRadius } from "ui_radius.slint"; import { GalleryStrings } from "device_strings.slint"; import { FontAwesomeIcons } from "common.slint"; import "../assets/fonts/message_spacing.ttf"; export enum AboutPage { overview, components, component, document, source } export struct AboutDocument { title: string, text: string } export struct AboutComponent { id: string, name: string, version: string, license: string, source-url: string, documents: [AboutDocument], } export global AboutStrings { out property software-license: GalleryStrings.tr("软件许可", "Software license", "Лицензия программы"); out property components: GalleryStrings.tr("开源组件", "Open-source components", "Компоненты с открытым кодом"); out property source: GalleryStrings.tr("源码与构建说明", "Source code and build instructions", "Исходный код и сборка"); out property notices: GalleryStrings.tr("许可与版权声明", "Licenses and copyright notices", "Лицензии и авторские права"); out property view-source: GalleryStrings.tr("查看源码", "View source code", "Открыть исходный код"); out property back: GalleryStrings.tr("返回", "Back", "Назад"); } export component AboutNavigationGroup inherits Rectangle { height: rows.preferred-height + 2px; background: WorkbenchStyle.surface; border-radius: UiRadius.control; border-width: 1px; border-color: WorkbenchStyle.separator; clip: true; rows := VerticalLayout { x: 1px; y: 1px; width: parent.width - 2px; height: self.preferred-height; padding: 0px; spacing: 0px; @children } } export component AboutRowSeparator inherits Rectangle { height: 1px; Rectangle { x: 14px; width: parent.width - 30px; height: 1px; background: WorkbenchStyle.separator; } } export component AboutNavigationRow inherits Rectangle { in property title; in property subtitle; in property icon; in property detail; in property grouped: false; in property external: false; callback clicked; min-width: 0px; min-height: max(44px, labels.preferred-height + 20px); background: touch.pressed ? WorkbenchStyle.pressed-surface : touch.has-hover ? WorkbenchStyle.hover-surface : WorkbenchStyle.surface; border-radius: root.grouped ? 0px : UiRadius.control; border-width: root.grouped ? 0px : 1px; border-color: WorkbenchStyle.separator; accessible-role: button; accessible-label: root.title + (root.subtitle != "" ? ", " + root.subtitle : "") + (root.detail != "" ? ", " + root.detail : ""); accessible-action-default => { root.clicked(); } if root.icon != "": Rectangle { x: 12px; y: (parent.height - self.height) / 2; width: 28px; height: 28px; border-radius: UiRadius.control; background: WorkbenchStyle.field-surface; Text { text: root.icon; font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 13px; color: WorkbenchStyle.muted; horizontal-alignment: center; vertical-alignment: center; accessible-role: none; } } labels := VerticalLayout { x: root.icon != "" ? 52px : 14px; y: (parent.height - self.height) / 2; width: parent.width - self.x - 36px - (root.detail != "" ? detail-label.width + 12px : 0px); height: self.preferred-height; spacing: 4px; Text { text: root.title; color: WorkbenchStyle.text; font-size: 13px; wrap: word-wrap; } if root.subtitle != "": Text { text: root.subtitle; color: WorkbenchStyle.muted; font-size: 11px; wrap: word-wrap; } } detail-label := Text { x: parent.width - self.width - 36px; height: parent.height; width: min(100px, self.preferred-width); text: root.detail; color: WorkbenchStyle.muted; font-size: 11px; vertical-alignment: center; overflow: elide; accessible-role: none; } Text { x: parent.width - 30px; width: 18px; height: parent.height; text: root.external ? FontAwesomeIcons.arrow-up-right-from-square : FontAwesomeIcons.angle-right; font-family: "Font Awesome 6 Free"; font-weight: 900; color: touch.has-hover ? WorkbenchStyle.accent : WorkbenchStyle.muted; font-size: 11px; vertical-alignment: center; horizontal-alignment: center; accessible-role: none; } touch := TouchArea { mouse-cursor: pointer; clicked => { root.clicked(); } } } export component AboutPanel inherits SettingsDialog { show-close-button: false; in-out property page: AboutPage.components; in-out property component-index: 0; private property document-index: 0; in property <[AboutComponent]> components; in property openfec-index: -1; in property revision; in property source-url; in property build-url; private property selected: root.components[root.component-index]; callback back; callback open-link(string); title: root.page == AboutPage.components ? AboutStrings.components : root.page == AboutPage.source ? AboutStrings.source : root.page == AboutPage.document ? AboutStrings.notices : root.selected.name; if root.page != AboutPage.document: ScrollView { x: 24px; y: root.content-top; width: parent.width - 48px + 22px; height: footer.y - self.y - 16px; viewport-width: self.visible-width; viewport-height: content.preferred-height; horizontal-scrollbar-policy: always-off; vertical-scrollbar-policy: as-needed; content := VerticalLayout { width: parent.width; height: self.preferred-height; padding: 0px; padding-right: 22px; spacing: 10px; if root.page == AboutPage.components: VerticalLayout { padding: 0px; spacing: 10px; Text { text: GalleryStrings.tr("以下列出本版本的主要开源组件。各组件遵循各自的开源许可。", "Main open-source components used in this build. Each component is covered by its own license.", "Основные открытые компоненты этой сборки. Каждый распространяется по своей лицензии."); color: WorkbenchStyle.muted; font-size: 12px; wrap: word-wrap; } Rectangle { height: notice.preferred-height + 32px; background: WorkbenchStyle.field-surface; border-radius: UiRadius.control; notice := VerticalLayout { x: 16px; y: 16px; width: parent.width - 32px; height: self.preferred-height; spacing: 12px; Text { text: GalleryStrings.tr("OpenFEC 告知", "OpenFEC notice", "Уведомление OpenFEC"); color: WorkbenchStyle.text; font-size: 14px; font-weight: 600; } Text { text: GalleryStrings.tr("本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。", "This application uses OpenFEC for forward error correction under CeCILL-C 1.0, which provides limited warranties and limits liability. See the full license for details.", "Приложение использует OpenFEC для прямого исправления ошибок по лицензии CeCILL-C 1.0 с ограниченной гарантией и ответственностью. Подробности — в тексте лицензии."); color: WorkbenchStyle.muted; font-size: 13px; wrap: word-wrap; font-family: "CrossDesk Message Spacing"; } Text { text: "(c) Copyright 2009 - 2012 INRIA - All rights reserved"; color: WorkbenchStyle.muted; font-size: 11px; wrap: word-wrap; font-family: "CrossDesk Message Spacing"; } if root.openfec-index >= 0: AboutNavigationRow { title: GalleryStrings.tr("OpenFEC 许可与源码", "OpenFEC license and source", "Лицензия и исходный код OpenFEC"); clicked => { root.component-index = root.openfec-index; root.page = AboutPage.component; } } } } for component[index] in root.components: VerticalLayout { padding: 0px; spacing: 0px; if component.id != "crossdesk" && component.id != "openfec": AboutNavigationRow { title: component.name; subtitle: component.version + " · " + component.license; clicked => { root.component-index = index; root.page = AboutPage.component; } } } } if root.page == AboutPage.component: VerticalLayout { padding: 0px; spacing: 12px; Text { text: root.selected.version + " · " + root.selected.license; color: WorkbenchStyle.muted; font-size: 12px; wrap: word-wrap; } AboutNavigationRow { title: AboutStrings.view-source; external: true; clicked => { root.open-link(root.selected.source-url); } } Text { text: AboutStrings.notices; color: WorkbenchStyle.text; font-size: 13px; font-weight: 600; } for document[index] in root.selected.documents: AboutNavigationRow { title: document.title; clicked => { root.document-index = index; root.page = AboutPage.document; } } if root.selected.documents.length == 0: Text { text: GalleryStrings.tr("本组件未内置许可文本,请通过“查看源码”查阅许可与版权声明。", "License text is not bundled for this component. Use View source code to read its license and copyright notices.", "Текст лицензии не включён. Откройте исходный код для просмотра лицензии и авторских прав."); color: WorkbenchStyle.muted; font-size: 12px; wrap: word-wrap; } } if root.page == AboutPage.source: VerticalLayout { padding: 0px; spacing: 12px; Text { text: root.revision != "" ? GalleryStrings.tr("源码版本:", "Source revision: ", "Ревизия исходного кода: ") + root.revision : GalleryStrings.tr("本版本源码信息暂不可用。", "Source revision is unavailable for this build.", "Ревизия исходного кода этой сборки недоступна."); color: WorkbenchStyle.text; font-size: 13px; wrap: word-wrap; } AboutNavigationRow { title: AboutStrings.view-source; external: true; clicked => { root.open-link(root.source-url); } } AboutNavigationRow { title: GalleryStrings.tr("构建说明", "Build instructions", "Инструкции по сборке"); external: true; clicked => { root.open-link(root.build-url); } } AboutNavigationRow { title: GalleryStrings.tr("项目主页", "Project website", "Сайт проекта"); external: true; clicked => { root.open-link("https://crossdesk.cn"); } } } } } if root.page == AboutPage.document: VerticalLayout { x: 24px; y: root.content-top; width: parent.width - 48px; height: footer.y - self.y - 16px; spacing: 12px; Text { text: root.selected.documents[root.document-index].title; color: WorkbenchStyle.text; font-size: 13px; font-weight: 600; wrap: word-wrap; } TextEdit { text: root.selected.documents[root.document-index].text; read-only: true; wrap: word-wrap; font-size: 14px; // Use the same 1.5em line spacing as other long-form reading views. font-family: "CrossDesk Message Spacing"; vertical-stretch: 1; key-pressed(event) => { if event.text == Key.Escape { root.back(); return accept; } return reject; } } } footer := VerticalLayout { x: 24px; y: parent.height - self.height - 24px; width: parent.width - 48px; height: self.preferred-height; spacing: 12px; Rectangle { height: 1px; background: WorkbenchStyle.separator; } HorizontalLayout { alignment: end; SettingsButton { text: AboutStrings.back; clicked => { root.back(); } } } } }