Add TabBar
This commit is contained in:
parent
7fe4e16d5c
commit
08a99217a2
13 changed files with 250 additions and 52 deletions
|
|
@ -6,6 +6,7 @@ https://github.com/huacnlee/gpui-app/assets/5518/ad103f02-697a-40ed-a876-8b13e42
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
- [x] Theming
|
||||||
- [ ] TextField
|
- [ ] TextField
|
||||||
- [x] Ctrl+a, e to move cursor to start/end
|
- [x] Ctrl+a, e to move cursor to start/end
|
||||||
- [x] Copy, Cut, Paste by keyboard
|
- [x] Copy, Cut, Paste by keyboard
|
||||||
|
|
@ -23,7 +24,9 @@ https://github.com/huacnlee/gpui-app/assets/5518/ad103f02-697a-40ed-a876-8b13e42
|
||||||
- [ ] Radio & RadioGroup
|
- [ ] Radio & RadioGroup
|
||||||
- [ ] Dropdown
|
- [ ] Dropdown
|
||||||
- [ ] Combobox
|
- [ ] Combobox
|
||||||
- [ ] Tabs
|
- [x] Tabs
|
||||||
|
- [x] Tab
|
||||||
|
- [x] TabBar
|
||||||
- [ ] Dockpanel & Splitter
|
- [ ] Dockpanel & Splitter
|
||||||
- [ ] List
|
- [ ] List
|
||||||
- [ ] Table
|
- [ ] Table
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ use gpui::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
disableable::{Clickable, Disableable, Selectable},
|
|
||||||
label::Label,
|
label::Label,
|
||||||
theme::{ActiveTheme, Colorize as _, Theme, ThemeMode},
|
theme::{ActiveTheme, Colorize as _, ThemeMode},
|
||||||
|
Clickable, Disableable, Selectable,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub enum ButtonRounded {
|
pub enum ButtonRounded {
|
||||||
|
|
|
||||||
|
|
@ -7,18 +7,20 @@ use gpui::{
|
||||||
use crate::{
|
use crate::{
|
||||||
disableable::Disableable,
|
disableable::Disableable,
|
||||||
label::Label,
|
label::Label,
|
||||||
selectable::Selection,
|
selectable::{Selectable, Selection},
|
||||||
stock::{h_flex, v_flex},
|
stock::{h_flex, v_flex},
|
||||||
theme::{ActiveTheme, Colorize as _},
|
theme::{ActiveTheme, Colorize as _},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type OnClick = Box<dyn Fn(&Selection, &mut WindowContext) + 'static>;
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct Checkbox {
|
pub struct Checkbox {
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
checked: Selection,
|
checked: Selection,
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
label: Option<SharedString>,
|
label: Option<SharedString>,
|
||||||
on_click: Option<Box<dyn Fn(&Selection, &mut WindowContext) + 'static>>,
|
on_click: Option<OnClick>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Checkbox {
|
impl Checkbox {
|
||||||
|
|
@ -55,6 +57,16 @@ impl Disableable for Checkbox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Selectable for Checkbox {
|
||||||
|
fn selected(self, selected: bool) -> Self {
|
||||||
|
self.checked(if selected {
|
||||||
|
Selection::Selected
|
||||||
|
} else {
|
||||||
|
Selection::Unselected
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl RenderOnce for Checkbox {
|
impl RenderOnce for Checkbox {
|
||||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
let theme = cx.theme();
|
let theme = cx.theme();
|
||||||
|
|
@ -120,6 +132,7 @@ impl RenderOnce for Checkbox {
|
||||||
|this, on_click| {
|
|this, on_click| {
|
||||||
this.on_click(move |_, cx| {
|
this.on_click(move |_, cx| {
|
||||||
on_click(&self.checked.inverse(), cx);
|
on_click(&self.checked.inverse(), cx);
|
||||||
|
cx.refresh()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
5
crates/ui/src/clickable.rs
Normal file
5
crates/ui/src/clickable.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
use gpui::{ClickEvent, WindowContext};
|
||||||
|
|
||||||
|
pub trait Clickable {
|
||||||
|
fn on_click(self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self;
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,3 @@
|
||||||
use gpui::{ClickEvent, WindowContext};
|
|
||||||
|
|
||||||
pub trait Disableable {
|
pub trait Disableable {
|
||||||
fn disabled(self, disabled: bool) -> Self;
|
fn disabled(self, disabled: bool) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Selectable {
|
|
||||||
fn selected(self, selected: bool) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Clickable {
|
|
||||||
fn on_click(self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,24 @@
|
||||||
|
mod clickable;
|
||||||
|
mod disableable;
|
||||||
|
mod icon;
|
||||||
|
mod platform;
|
||||||
|
mod prelude;
|
||||||
|
mod selectable;
|
||||||
|
mod stock;
|
||||||
|
mod styled_ext;
|
||||||
|
|
||||||
pub mod button;
|
pub mod button;
|
||||||
pub mod checkbox;
|
pub mod checkbox;
|
||||||
pub mod disableable;
|
|
||||||
pub mod label;
|
pub mod label;
|
||||||
mod prelude;
|
|
||||||
pub mod selectable;
|
|
||||||
mod stock;
|
|
||||||
pub mod story;
|
pub mod story;
|
||||||
mod styled_ext;
|
|
||||||
pub mod text_field;
|
pub mod text_field;
|
||||||
pub mod theme;
|
pub mod theme;
|
||||||
pub mod title_bar;
|
pub mod title_bar;
|
||||||
pub use styled_ext::StyledExt;
|
pub use styled_ext::StyledExt;
|
||||||
mod icon;
|
pub mod tab;
|
||||||
mod platform;
|
|
||||||
|
pub use clickable::Clickable;
|
||||||
|
pub use disableable::Disableable;
|
||||||
|
pub use selectable::Selectable;
|
||||||
|
|
||||||
pub use icon::*;
|
pub use icon::*;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use gpui::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
button::{Button, ButtonSize, ButtonStyle},
|
button::{Button, ButtonSize, ButtonStyle},
|
||||||
disableable::{Clickable, Disableable as _},
|
Clickable, Disableable as _,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::story_case;
|
use super::story_case;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, ClickEvent, IntoElement, ParentElement, Render, Styled, ViewContext, WindowContext,
|
div, ClickEvent, IntoElement, ParentElement, Render, RenderOnce, Styled, ViewContext,
|
||||||
|
WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -11,11 +12,22 @@ use crate::{
|
||||||
|
|
||||||
use super::story_case;
|
use super::story_case;
|
||||||
|
|
||||||
pub struct CheckboxStory {}
|
#[derive(IntoElement)]
|
||||||
|
pub struct CheckboxStory {
|
||||||
|
check1: Checkbox,
|
||||||
|
check1_1: Checkbox,
|
||||||
|
}
|
||||||
|
|
||||||
impl CheckboxStory {
|
impl CheckboxStory {
|
||||||
pub(crate) fn new(_cx: &mut WindowContext) -> Self {
|
pub(crate) fn new(cx: &mut WindowContext) -> Self {
|
||||||
Self {}
|
Self {
|
||||||
|
check1: Checkbox::new("check1", cx)
|
||||||
|
.checked(Selection::Unselected)
|
||||||
|
.on_click(Self::on_click),
|
||||||
|
check1_1: Checkbox::new("check1_1", cx)
|
||||||
|
.checked(Selection::Indeterminate)
|
||||||
|
.on_click(Self::on_click),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
|
@ -24,8 +36,8 @@ impl CheckboxStory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for CheckboxStory {
|
impl RenderOnce for CheckboxStory {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
story_case(
|
story_case(
|
||||||
"Checkbox",
|
"Checkbox",
|
||||||
"A control that allows the user to toggle between checked and not checked.",
|
"A control that allows the user to toggle between checked and not checked.",
|
||||||
|
|
@ -35,16 +47,8 @@ impl Render for CheckboxStory {
|
||||||
h_flex()
|
h_flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.gap_4()
|
.gap_4()
|
||||||
.child(
|
.child(self.check1)
|
||||||
Checkbox::new("check1", cx)
|
.child(self.check1_1)
|
||||||
.checked(Selection::Unselected)
|
|
||||||
.on_click(Self::on_click),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Checkbox::new("check1_1", cx)
|
|
||||||
.checked(Selection::Indeterminate)
|
|
||||||
.on_click(Self::on_click),
|
|
||||||
)
|
|
||||||
.child(
|
.child(
|
||||||
Checkbox::new("check1_2", cx)
|
Checkbox::new("check1_2", cx)
|
||||||
.checked(Selection::Selected)
|
.checked(Selection::Selected)
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,26 @@
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::{
|
||||||
|
fmt::{Display, Formatter},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use checkbox_story::CheckboxStory;
|
use checkbox_story::CheckboxStory;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, prelude::FluentBuilder as _, px, AnyElement, IntoElement, ParentElement, Render,
|
div, prelude::FluentBuilder as _, px, AnyElement, IntoElement, ParentElement, Render,
|
||||||
RenderOnce, SharedString, Styled as _, View, ViewContext, VisualContext, WindowContext,
|
RenderOnce, SharedString, StatefulInteractiveElement as _, Styled as _, View, ViewContext,
|
||||||
|
VisualContext, WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod button_story;
|
mod button_story;
|
||||||
mod checkbox_story;
|
mod checkbox_story;
|
||||||
mod input_story;
|
mod input_story;
|
||||||
|
|
||||||
use crate::{button::Button, disableable::Clickable as _, label::Label};
|
use crate::{
|
||||||
|
button::Button,
|
||||||
|
label::Label,
|
||||||
|
tab::{Tab, TabBar},
|
||||||
|
Selectable,
|
||||||
|
};
|
||||||
|
|
||||||
use button_story::ButtonStory;
|
use button_story::ButtonStory;
|
||||||
use input_story::InputStory;
|
use input_story::InputStory;
|
||||||
|
|
@ -83,7 +92,6 @@ pub struct Stories {
|
||||||
|
|
||||||
button_story: View<ButtonStory>,
|
button_story: View<ButtonStory>,
|
||||||
input_story: View<InputStory>,
|
input_story: View<InputStory>,
|
||||||
checkbox_story: View<CheckboxStory>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stories {
|
impl Stories {
|
||||||
|
|
@ -92,7 +100,6 @@ impl Stories {
|
||||||
active: StoryType::Button,
|
active: StoryType::Button,
|
||||||
button_story: cx.new_view(|cx| ButtonStory {}),
|
button_story: cx.new_view(|cx| ButtonStory {}),
|
||||||
input_story: cx.new_view(|cx| InputStory::new(cx)),
|
input_story: cx.new_view(|cx| InputStory::new(cx)),
|
||||||
checkbox_story: cx.new_view(|cx| CheckboxStory::new(cx)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,13 +113,18 @@ impl Stories {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_story_buttons(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render_story_buttons(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
let tabs = vec![
|
||||||
|
self.swith_button("story-button", StoryType::Button, cx),
|
||||||
|
self.swith_button("story-input", StoryType::Input, cx),
|
||||||
|
self.swith_button("story-checkbox", StoryType::Checkbox, cx),
|
||||||
|
];
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.gap_4()
|
.gap_4()
|
||||||
.child(self.swith_button("story-button", StoryType::Button, cx))
|
.w_full()
|
||||||
.child(self.swith_button("story-input", StoryType::Input, cx))
|
.child(TabBar::new("story-tabs").children(tabs))
|
||||||
.child(self.swith_button("story-checkbox", StoryType::Checkbox, cx))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn swith_button(
|
fn swith_button(
|
||||||
|
|
@ -122,18 +134,20 @@ impl Stories {
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let name = format!("{}", ty);
|
let name = format!("{}", ty);
|
||||||
|
let is_active = ty == self.active;
|
||||||
|
|
||||||
Button::new(SharedString::from(id.to_string()), name)
|
Tab::new(SharedString::from(id.to_string()), name)
|
||||||
|
.selected(is_active)
|
||||||
.on_click(cx.listener(move |this, _, cx| {
|
.on_click(cx.listener(move |this, _, cx| {
|
||||||
this.set_active(ty, cx);
|
this.set_active(ty, cx);
|
||||||
}))
|
}))
|
||||||
.style(crate::button::ButtonStyle::Secondary)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for Stories {
|
impl Render for Stories {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
div()
|
div()
|
||||||
|
.w_full()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.gap_4()
|
.gap_4()
|
||||||
|
|
@ -141,7 +155,7 @@ impl Render for Stories {
|
||||||
.map(|this| match self.active {
|
.map(|this| match self.active {
|
||||||
StoryType::Button => this.child(self.button_story.clone()),
|
StoryType::Button => this.child(self.button_story.clone()),
|
||||||
StoryType::Input => this.child(self.input_story.clone()),
|
StoryType::Input => this.child(self.input_story.clone()),
|
||||||
StoryType::Checkbox => this.child(self.checkbox_story.clone()),
|
StoryType::Checkbox => this.child(CheckboxStory::new(cx).into_any_element()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
crates/ui/src/tab.rs
Normal file
5
crates/ui/src/tab.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
mod tab;
|
||||||
|
mod tab_bar;
|
||||||
|
|
||||||
|
pub use tab::*;
|
||||||
|
pub use tab_bar::*;
|
||||||
87
crates/ui/src/tab/tab.rs
Normal file
87
crates/ui/src/tab/tab.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
use crate::selectable::Selectable;
|
||||||
|
use crate::theme::{ActiveTheme, Colorize};
|
||||||
|
use gpui::prelude::FluentBuilder as _;
|
||||||
|
use gpui::{
|
||||||
|
div, AnyElement, Div, IntoElement, ParentElement as _, RenderOnce, SharedString, Stateful,
|
||||||
|
StatefulInteractiveElement, WindowContext,
|
||||||
|
};
|
||||||
|
use gpui::{InteractiveElement, Styled as _};
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
pub struct Tab {
|
||||||
|
base: Stateful<Div>,
|
||||||
|
label: SharedString,
|
||||||
|
prefix: Option<AnyElement>,
|
||||||
|
suffix: Option<AnyElement>,
|
||||||
|
disabled: bool,
|
||||||
|
selected: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tab {
|
||||||
|
pub fn new(id: impl Into<SharedString>, label: impl Into<SharedString>) -> Self {
|
||||||
|
Self {
|
||||||
|
base: div().id(id.into()),
|
||||||
|
label: label.into(),
|
||||||
|
disabled: false,
|
||||||
|
selected: false,
|
||||||
|
prefix: None,
|
||||||
|
suffix: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the left side of the tab
|
||||||
|
pub fn prefix(mut self, prefix: impl Into<AnyElement>) -> Self {
|
||||||
|
self.prefix = Some(prefix.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the right side of the tab
|
||||||
|
pub fn suffix(mut self, suffix: impl Into<AnyElement>) -> Self {
|
||||||
|
self.suffix = Some(suffix.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Selectable for Tab {
|
||||||
|
fn selected(mut self, selected: bool) -> Self {
|
||||||
|
self.selected = selected;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InteractiveElement for Tab {
|
||||||
|
fn interactivity(&mut self) -> &mut gpui::Interactivity {
|
||||||
|
self.base.interactivity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatefulInteractiveElement for Tab {}
|
||||||
|
|
||||||
|
impl RenderOnce for Tab {
|
||||||
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
|
let theme = cx.theme();
|
||||||
|
|
||||||
|
self.base
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.gap_2()
|
||||||
|
.h_full()
|
||||||
|
.cursor_pointer()
|
||||||
|
.py_1()
|
||||||
|
.px_3()
|
||||||
|
.min_w_16()
|
||||||
|
.text_color(theme.muted_foreground)
|
||||||
|
.bg(theme.muted)
|
||||||
|
.when(self.selected, |this| {
|
||||||
|
this.text_color(theme.foreground)
|
||||||
|
.bg(theme.background)
|
||||||
|
.rounded_sm()
|
||||||
|
})
|
||||||
|
.when(self.disabled, |this| {
|
||||||
|
this.text_color(theme.foreground.opacity(0.5))
|
||||||
|
})
|
||||||
|
.when_some(self.prefix, |this, prefix| this.child(prefix))
|
||||||
|
.child(self.label.clone())
|
||||||
|
.when_some(self.suffix, |this, suffix| this.child(suffix))
|
||||||
|
}
|
||||||
|
}
|
||||||
71
crates/ui/src/tab/tab_bar.rs
Normal file
71
crates/ui/src/tab/tab_bar.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
use crate::stock::h_flex;
|
||||||
|
use crate::theme::ActiveTheme;
|
||||||
|
use gpui::prelude::FluentBuilder as _;
|
||||||
|
use gpui::{
|
||||||
|
div, AnyElement, Div, IntoElement, ParentElement, RenderOnce, ScrollHandle, SharedString,
|
||||||
|
StatefulInteractiveElement as _, WindowContext,
|
||||||
|
};
|
||||||
|
use gpui::{InteractiveElement, Styled as _};
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
pub struct TabBar {
|
||||||
|
base: Div,
|
||||||
|
id: SharedString,
|
||||||
|
scroll_handle: Option<ScrollHandle>,
|
||||||
|
children: SmallVec<[AnyElement; 2]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TabBar {
|
||||||
|
pub fn new(id: impl Into<SharedString>) -> Self {
|
||||||
|
Self {
|
||||||
|
base: div(),
|
||||||
|
id: id.into(),
|
||||||
|
children: SmallVec::new(),
|
||||||
|
scroll_handle: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn track_scroll(mut self, scroll_handle: ScrollHandle) -> Self {
|
||||||
|
self.scroll_handle = Some(scroll_handle);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParentElement for TabBar {
|
||||||
|
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||||
|
self.children.extend(elements)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for TabBar {
|
||||||
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
|
let theme = cx.theme();
|
||||||
|
|
||||||
|
self.base
|
||||||
|
.id(self.id)
|
||||||
|
.group("tab_bar")
|
||||||
|
.flex()
|
||||||
|
.flex_none()
|
||||||
|
.items_center()
|
||||||
|
.w_full()
|
||||||
|
.h_10()
|
||||||
|
.p_1()
|
||||||
|
.bg(theme.muted)
|
||||||
|
.text_color(theme.muted_foreground)
|
||||||
|
.rounded_md()
|
||||||
|
.relative()
|
||||||
|
.overflow_x_hidden()
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.id("tabs")
|
||||||
|
.flex_grow()
|
||||||
|
.overflow_x_scroll()
|
||||||
|
.when_some(self.scroll_handle, |cx, scroll_handle| {
|
||||||
|
cx.track_scroll(&scroll_handle)
|
||||||
|
})
|
||||||
|
.children(self.children),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,12 +4,11 @@ use prelude::FluentBuilder as _;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use ui::{
|
use ui::{
|
||||||
button::Button,
|
button::Button,
|
||||||
disableable::Clickable as _,
|
|
||||||
label::Label,
|
label::Label,
|
||||||
story::Stories,
|
story::Stories,
|
||||||
theme::{ActiveTheme, Theme},
|
theme::{ActiveTheme, Theme},
|
||||||
title_bar::TitleBar,
|
title_bar::TitleBar,
|
||||||
StyledExt as _,
|
Clickable as _, StyledExt as _,
|
||||||
};
|
};
|
||||||
use util::ResultExt as _;
|
use util::ResultExt as _;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue