import { Theme } from "theme.slint"; import { IconButton } from "widgets.slint"; export struct TabInfo { id: string, title: string, kind: string, // "welcome" | "terminal" connected: bool, } // --- SingleTab ------------------------------------------------------------- component SingleTab inherits Rectangle { in property info; in property active; in property shown: true; in property app-background-active: false; in property ui-opacity: 0.72; callback selected(); callback closed(); height: shown ? 30px : 0px; property title-width: min(128px, max(34px, title-probe.preferred-width)); property tab-width: info.kind == "welcome" ? max(72px, title-width + 34px) : max(74px, title-width + 62px); width: shown ? min(176px, tab-width) : 0px; visible: shown; horizontal-stretch: 0; vertical-stretch: 0; border-radius: Theme.radius-sm; background: active ? (root.app-background-active ? Theme.bg-panel.with-alpha(root.ui-opacity) : Theme.bg-panel) : (touch.has-hover ? Theme.bg-hover : transparent); animate background { duration: 120ms; } // Declared FIRST so the HorizontalLayout's children (close button) are at // a higher z-order and receive their own click events. touch := TouchArea { mouse-cursor: pointer; clicked => { root.selected(); } } title-probe := Text { text: info.title; font-size: Theme.fs-sm; opacity: 0; x: -1000px; y: -1000px; } HorizontalLayout { height: parent.height; padding-left: 10px; padding-right: 6px; spacing: 8px; alignment: center; // Status indicator Rectangle { width: 8px; height: parent.height; Rectangle { y: (parent.height - self.height) / 2; width: parent.width; height: 8px; border-radius: 4px; background: info.kind == "welcome" ? Theme.text-muted : (info.connected ? Theme.success : Theme.warning); } } Text { text: info.title; height: parent.height; color: active ? Theme.text-primary : Theme.text-secondary; font-size: Theme.fs-sm; vertical-alignment: center; overflow: elide; horizontal-stretch: 1; } // Only show close button for non-welcome tabs if info.kind != "welcome" : Rectangle { width: 20px; height: parent.height; IconButton { y: (parent.height - self.height) / 2; glyph: "×"; size: 20px; clicked => { root.closed(); } } } } } // --- TabBar ---------------------------------------------------------------- export component TabBar inherits Rectangle { in property <[TabInfo]> tabs; in property active-id; in-out property visible-start: 0; in property app-background-active: false; in property ui-opacity: 0.72; property visible-count: 5; callback tab-selected(string); callback tab-closed(string); callback new-tab(); callback previous-tab(); callback next-tab(); height: 36px; background: root.app-background-active ? Theme.bg-panel-alt.with-alpha(root.ui-opacity) : Theme.bg-panel-alt; HorizontalLayout { height: parent.height; padding-left: 3px; padding-right: 100px; // reserve the overlaid theme/download/settings buttons padding-top: 3px; padding-bottom: 3px; spacing: 2px; Rectangle { width: 30px; height: 30px; border-radius: Theme.radius-sm; background: root.active-id == "welcome" ? Theme.bg-panel : (folder-ta.has-hover ? Theme.bg-hover : transparent); folder-ta := TouchArea { mouse-cursor: pointer; clicked => { root.new-tab(); } } Text { width: parent.width; height: parent.height; text: "📂"; color: root.active-id == "welcome" ? Theme.text-primary : Theme.text-secondary; font-size: Theme.fs-md; horizontal-alignment: center; vertical-alignment: center; } } Rectangle { width: 56px; height: 30px; vertical-stretch: 0; HorizontalLayout { height: parent.height; spacing: 0; Rectangle { width: 28px; height: parent.height; border-radius: Theme.radius-sm; background: prev-ta.has-hover ? Theme.bg-hover : transparent; prev-ta := TouchArea { mouse-cursor: pointer; clicked => { root.previous-tab(); } } Text { width: parent.width; height: parent.height; text: "[<]"; color: Theme.text-secondary; font-size: Theme.fs-xs; horizontal-alignment: center; vertical-alignment: center; } } Rectangle { width: 28px; height: parent.height; border-radius: Theme.radius-sm; background: next-ta.has-hover ? Theme.bg-hover : transparent; next-ta := TouchArea { mouse-cursor: pointer; clicked => { root.next-tab(); } } Text { width: parent.width; height: parent.height; text: "[>]"; color: Theme.text-secondary; font-size: Theme.fs-xs; horizontal-alignment: center; vertical-alignment: center; } } } } Rectangle { horizontal-stretch: 1; height: 30px; clip: true; tabs-flick := Flickable { width: parent.width; height: parent.height; viewport-width: max(self.width, tabs-row.preferred-width); viewport-height: self.height; interactive: true; tabs-row := HorizontalLayout { height: parent.height; spacing: 0px; for tab[i] in root.tabs : SingleTab { shown: i >= root.visible-start && i < root.visible-start + root.visible-count; info: tab; active: tab.id == root.active-id; app-background-active: root.app-background-active; ui-opacity: root.ui-opacity; selected => { root.tab-selected(tab.id); } closed => { root.tab-closed(tab.id); } } } } } } }