20260619
This commit is contained in:
223
ui/tabs.slint
Normal file
223
ui/tabs.slint
Normal file
@@ -0,0 +1,223 @@
|
||||
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 <TabInfo> info;
|
||||
in property <bool> active;
|
||||
in property <bool> shown: true;
|
||||
in property <bool> app-background-active: false;
|
||||
in property <float> ui-opacity: 0.72;
|
||||
callback selected();
|
||||
callback closed();
|
||||
|
||||
height: shown ? 30px : 0px;
|
||||
property <length> title-width: min(128px, max(34px, title-probe.preferred-width));
|
||||
property <length> 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 <string> active-id;
|
||||
in-out property <int> visible-start: 0;
|
||||
in property <bool> app-background-active: false;
|
||||
in property <float> ui-opacity: 0.72;
|
||||
property <int> 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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user