dock: Add id to DockArea and improve DockArea init. (#204)

- Export add_panel_at method for TabPanel to split_panel.
- Fix resizeable_panel init size support.
- Fix panel resize to let panel that have initial size will not auto resize on window resize.
- Fix resize_handle sometimes will invisibly bug.
- Update TabPanel new to support give parent StackPanel.
This commit is contained in:
Jason Lee 2024-09-02 21:27:19 +08:00 committed by GitHub
parent f611b599aa
commit 8ece86cd0c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 263 additions and 85 deletions

View file

@ -18,7 +18,7 @@ use ui::{
modal::Modal,
popup_menu::PopupMenuExt,
theme::{ActiveTheme, Colorize as _, Theme},
ContextModal, IconName, Root, Sizable,
ContextModal, IconName, Placement, Root, Sizable,
};
use crate::app_state::AppState;
@ -52,35 +52,49 @@ impl StoryWorkspace {
.detach();
let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Horizontal, cx));
let dock_area = cx.new_view(|cx| DockArea::new(stack_panel.clone(), cx));
let dock_area = cx.new_view(|cx| DockArea::new("main-dock", stack_panel.clone(), cx));
let weak_dock_area = dock_area.downgrade();
let tab_panel = cx.new_view(|cx| TabPanel::new(weak_dock_area.clone(), cx));
let right_tab_panel = cx.new_view(|cx| TabPanel::new(weak_dock_area.clone(), cx));
let right_tab_panel1 = cx.new_view(|cx| TabPanel::new(weak_dock_area.clone(), cx));
let center_tab_panel =
cx.new_view(|cx| TabPanel::new(Some(stack_panel.clone()), weak_dock_area.clone(), cx));
let left_tab_panel =
cx.new_view(|cx| TabPanel::new(Some(stack_panel.clone()), weak_dock_area.clone(), cx));
let right_tab_panel =
cx.new_view(|cx| TabPanel::new(Some(stack_panel.clone()), weak_dock_area.clone(), cx));
stack_panel.update(cx, |view, cx| {
view.add_panel(tab_panel.clone(), None, weak_dock_area.clone(), cx);
let stock_panel1 = cx.new_view(|cx| StackPanel::new(Axis::Vertical, cx));
let left_stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Vertical, cx));
left_stack_panel.update(cx, |view, cx| {
view.add_panel(left_tab_panel.clone(), None, weak_dock_area.clone(), cx);
});
view.add_panel(
stock_panel1.clone(),
Some(px(380.)),
left_stack_panel.clone(),
Some(px(300.)),
weak_dock_area.clone(),
cx,
);
stock_panel1.update(cx, |view, cx| {
view.add_panel(center_tab_panel.clone(), None, weak_dock_area.clone(), cx);
let right_stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Vertical, cx));
right_stack_panel.update(cx, |view, cx| {
view.add_panel(right_tab_panel.clone(), None, weak_dock_area.clone(), cx);
view.add_panel(right_tab_panel1.clone(), None, weak_dock_area.clone(), cx);
})
});
view.add_panel(
right_stack_panel.clone(),
Some(px(340.)),
weak_dock_area.clone(),
cx,
);
});
StoryContainer::add_panel(
"Buttons",
"Displays a button or a component that looks like a button.",
ButtonStory::view(cx).into(),
tab_panel.clone(),
center_tab_panel.clone(),
None,
None,
false,
cx,
)
@ -90,7 +104,9 @@ impl StoryWorkspace {
"Input",
"A control that allows the user to input text.",
InputStory::view(cx).into(),
tab_panel.clone(),
center_tab_panel.clone(),
None,
None,
false,
cx,
)
@ -100,7 +116,9 @@ impl StoryWorkspace {
"Text",
"Links, paragraphs, checkboxes, and more.",
TextStory::view(cx).into(),
tab_panel.clone(),
center_tab_panel.clone(),
Some(Placement::Bottom),
Some(px(200.)),
true,
cx,
)
@ -110,7 +128,9 @@ impl StoryWorkspace {
"Switch",
"A control that allows the user to toggle between two states.",
SwitchStory::view(cx).into(),
tab_panel.clone(),
center_tab_panel.clone(),
None,
None,
true,
cx,
)
@ -120,7 +140,9 @@ impl StoryWorkspace {
"Dropdowns",
"Displays a list of options for the user to pick from—triggered by a button.",
DropdownStory::new(cx).into(),
tab_panel.clone(),
center_tab_panel.clone(),
None,
None,
true,
cx,
)
@ -130,7 +152,9 @@ impl StoryWorkspace {
"Modal",
"Modal & Drawer use examples",
ModalStory::view(cx).into(),
tab_panel.clone(),
center_tab_panel.clone(),
None,
None,
true,
cx,
)
@ -140,7 +164,9 @@ impl StoryWorkspace {
"Popup",
"A popup displays content on top of the main page.",
PopupStory::view(cx).into(),
tab_panel.clone(),
center_tab_panel.clone(),
None,
None,
true,
cx,
)
@ -150,7 +176,9 @@ impl StoryWorkspace {
"Tooltip",
"Displays a short message when users hover over an element.",
TooltipStory::view(cx).into(),
tab_panel.clone(),
right_tab_panel.clone(),
Some(Placement::Top),
None,
true,
cx,
)
@ -160,7 +188,9 @@ impl StoryWorkspace {
"List",
"A list displays a series of items.",
ListStory::view(cx).into(),
tab_panel.clone(),
left_tab_panel.clone(),
None,
None,
true,
cx,
)
@ -170,7 +200,9 @@ impl StoryWorkspace {
"Icon",
"Icon use examples",
IconStory::view(cx).into(),
tab_panel.clone(),
left_tab_panel.clone(),
Some(Placement::Bottom),
Some(px(200.)),
true,
cx,
)
@ -180,7 +212,9 @@ impl StoryWorkspace {
"Image",
"Render SVG image and Chart",
ImageStory::view(cx).into(),
right_tab_panel1.clone(),
right_tab_panel.clone(),
None,
None,
true,
cx,
)
@ -198,7 +232,9 @@ impl StoryWorkspace {
"Table",
"Powerful table and datagrids built.",
TableStory::view(cx).into(),
tab_panel.clone(),
center_tab_panel.clone(),
None,
None,
true,
cx,
)
@ -208,8 +244,10 @@ impl StoryWorkspace {
"Progress",
"Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.",
ProgressStory::view(cx).into(),
tab_panel.clone(),
true,
center_tab_panel.clone(),
Some(Placement::Bottom),
Some(px(200.)),
true,
cx,
)
.detach();
@ -218,7 +256,9 @@ impl StoryWorkspace {
"Resizable",
"Accessible resizable panel groups and layouts with keyboard support.",
ResizableStory::view(cx).into(),
tab_panel.clone(),
center_tab_panel.clone(),
None,
None,
true,
cx,
)
@ -228,7 +268,9 @@ impl StoryWorkspace {
"Scrollable",
"A scrollable area with scroll bar.",
ScrollableStory::view(cx).into(),
tab_panel.clone(),
center_tab_panel.clone(),
None,
None,
true,
cx,
)
@ -238,7 +280,9 @@ impl StoryWorkspace {
"Calendar",
"A calendar component.",
CalendarStory::view(cx).into(),
right_tab_panel.clone(),
center_tab_panel.clone(),
None,
None,
true,
cx,
)

View file

@ -38,7 +38,7 @@ pub use webview_story::WebViewStory;
use gpui::{
actions, div, prelude::FluentBuilder as _, px, AnyView, AppContext, Div, EventEmitter,
FocusableView, InteractiveElement, IntoElement, ParentElement, Render, SharedString,
FocusableView, InteractiveElement, IntoElement, ParentElement, Pixels, Render, SharedString,
StatefulInteractiveElement, Styled as _, Task, View, ViewContext, VisualContext, WindowContext,
};
@ -49,7 +49,7 @@ use ui::{
h_flex,
label::Label,
popup_menu::PopupMenu,
v_flex,
v_flex, Placement,
};
pub fn init(cx: &mut AppContext) {
@ -120,11 +120,14 @@ impl StoryContainer {
}
}
#[allow(clippy::too_many_arguments)]
pub fn add_panel(
name: impl Into<SharedString>,
description: impl Into<SharedString>,
story: AnyView,
tab_panel: View<TabPanel>,
placement: Option<Placement>,
size: Option<Pixels>,
closeable: bool,
cx: &mut WindowContext,
) -> Task<Result<View<Self>>> {
@ -135,7 +138,11 @@ impl StoryContainer {
tab_panel.update(&mut cx, |panel, cx| {
let view =
cx.new_view(|cx| Self::new(name, description, closeable, cx).story(story));
panel.add_panel(Arc::new(view.clone()), cx);
if let Some(placement) = placement {
panel.add_panel_at(Arc::new(view.clone()), placement, size, cx);
} else {
panel.add_panel(Arc::new(view.clone()), cx);
}
view
})
})

View file

@ -4,7 +4,7 @@ mod tab_panel;
use gpui::{
actions, div, prelude::FluentBuilder, AnyWeakView, InteractiveElement as _, IntoElement,
ParentElement as _, Render, Styled, View, ViewContext,
ParentElement as _, Render, SharedString, Styled, View, ViewContext,
};
pub use panel::*;
pub use stack_panel::*;
@ -14,18 +14,29 @@ actions!(dock, [ToggleZoom, ClosePanel]);
/// The main area of the dock.
pub struct DockArea {
id: SharedString,
root: View<StackPanel>,
zoom_view: Option<AnyWeakView>,
}
impl DockArea {
pub fn new(root: View<StackPanel>, _cx: &mut ViewContext<Self>) -> Self {
pub fn new(
id: impl Into<SharedString>,
root: View<StackPanel>,
_cx: &mut ViewContext<Self>,
) -> Self {
Self {
id: id.into(),
root,
zoom_view: None,
}
}
/// Returns the ID of the dock area.
pub fn id(&self) -> SharedString {
self.id.clone()
}
/// Toggles the zoom view.
pub fn toggle_zoom<P: Panel>(&mut self, panel: View<P>, cx: &mut ViewContext<Self>) {
if self.zoom_view.is_some() {
@ -35,6 +46,25 @@ impl DockArea {
}
cx.notify();
}
/// Returns the root stack panel.
pub fn root(&self) -> View<StackPanel> {
self.root.clone()
}
/// Return the index of the panel.
pub fn index_of_panel<P: Panel>(
&self,
panel: View<P>,
cx: &mut ViewContext<Self>,
) -> Option<usize> {
self.root.read(cx).index_of_panel(panel)
}
/// Return the existing panel by type.
pub fn panel<P: Panel>(&self, cx: &mut ViewContext<Self>) -> Option<View<P>> {
self.root.read(cx).panel::<P>(cx)
}
}
impl Render for DockArea {

View file

@ -11,7 +11,7 @@ use super::{DockArea, Panel, PanelEvent, PanelView, TabPanel};
use gpui::{
prelude::FluentBuilder as _, Axis, DismissEvent, Entity, EventEmitter, FocusHandle,
FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, View, ViewContext,
VisualContext, WeakView,
VisualContext, WeakView, WindowContext,
};
use smallvec::SmallVec;
@ -62,6 +62,24 @@ impl StackPanel {
.position(|p| p.view().entity_id() == entity_id)
}
/// Return the existing panel view by type.
pub fn panel<P>(&self, cx: &WindowContext) -> Option<View<P>>
where
P: Panel,
{
self.panels.iter().find_map(|p| {
if let Ok(p) = p.view().downcast::<P>() {
Some(p)
} else {
if let Ok(tab) = p.view().downcast::<TabPanel>() {
tab.read(cx).panel(cx)
} else {
None
}
}
})
}
/// Add a panel at the end of the stack.
pub fn add_panel<P>(
&mut self,
@ -76,19 +94,35 @@ impl StackPanel {
}
pub fn add_panel_at<P>(
&mut self,
panel: View<P>,
placement: Placement,
size: Option<Pixels>,
dock_area: WeakView<DockArea>,
cx: &mut ViewContext<Self>,
) where
P: Panel,
{
self.insert_panel_at(panel, self.panels_len(), placement, size, dock_area, cx);
}
pub fn insert_panel_at<P>(
&mut self,
panel: View<P>,
ix: usize,
placement: Placement,
size: Option<Pixels>,
dock_area: WeakView<DockArea>,
cx: &mut ViewContext<Self>,
) where
P: Panel,
{
match placement {
Placement::Top | Placement::Left => self.insert_panel_before(panel, ix, dock_area, cx),
Placement::Top | Placement::Left => {
self.insert_panel_before(panel, ix, size, dock_area, cx)
}
Placement::Right | Placement::Bottom => {
self.insert_panel_after(panel, ix, dock_area, cx)
self.insert_panel_after(panel, ix, size, dock_area, cx)
}
}
}
@ -98,12 +132,13 @@ impl StackPanel {
&mut self,
panel: View<P>,
ix: usize,
size: Option<Pixels>,
dock_area: WeakView<DockArea>,
cx: &mut ViewContext<Self>,
) where
P: Panel,
{
self.insert_panel(panel, ix, None, dock_area, cx);
self.insert_panel(panel, ix, size, dock_area, cx);
}
/// Insert a panel after the index.
@ -111,12 +146,13 @@ impl StackPanel {
&mut self,
panel: View<P>,
ix: usize,
size: Option<Pixels>,
dock_area: WeakView<DockArea>,
cx: &mut ViewContext<Self>,
) where
P: Panel,
{
self.insert_panel(panel, ix + 1, None, dock_area, cx);
self.insert_panel(panel, ix + 1, size, dock_area, cx);
}
fn new_resizable_panel<P>(panel: View<P>, size: Option<Pixels>) -> ResizablePanel
@ -176,6 +212,12 @@ impl StackPanel {
})
.detach();
let ix = if ix > self.panels.len() {
self.panels.len()
} else {
ix
};
self.panels.insert(ix, Arc::new(panel.clone()));
self.panel_group.update(cx, |view, cx| {
view.insert_child(Self::new_resizable_panel(panel, size), ix, cx)

View file

@ -3,8 +3,8 @@ use std::sync::Arc;
use gpui::{
div, prelude::FluentBuilder, rems, AnchorCorner, AppContext, DefiniteLength, DismissEvent,
DragMoveEvent, Empty, EventEmitter, FocusHandle, FocusableView, InteractiveElement as _,
IntoElement, ParentElement, Render, ScrollHandle, StatefulInteractiveElement, Styled, View,
ViewContext, VisualContext as _, WeakView, WindowContext,
IntoElement, ParentElement, Pixels, Render, ScrollHandle, StatefulInteractiveElement, Styled,
View, ViewContext, VisualContext as _, WeakView, WindowContext,
};
use rust_i18n::t;
@ -71,11 +71,15 @@ pub struct TabPanel {
}
impl TabPanel {
pub fn new(dock_area: WeakView<DockArea>, cx: &mut ViewContext<Self>) -> Self {
pub fn new(
stack_panel: Option<View<StackPanel>>,
dock_area: WeakView<DockArea>,
cx: &mut ViewContext<Self>,
) -> Self {
Self {
focus_handle: cx.focus_handle(),
dock_area,
stack_panel: None,
stack_panel,
panels: Vec::new(),
active_ix: 0,
tab_bar_scroll_handle: ScrollHandle::new(),
@ -99,6 +103,21 @@ impl TabPanel {
cx.notify();
}
/// Return the existing panel view by type.
pub fn panel<P: Panel>(&self, cx: &WindowContext) -> Option<View<P>> {
self.panels.iter().find_map(|p| {
if let Ok(p) = p.view().downcast::<P>() {
Some(p)
} else {
if let Ok(stack) = p.view().downcast::<StackPanel>() {
stack.read(cx).panel::<P>(cx)
} else {
None
}
}
})
}
/// Add a panel to the end of the tabs
pub fn add_panel(&mut self, panel: Arc<dyn PanelView>, cx: &mut ViewContext<Self>) {
if self
@ -115,6 +134,19 @@ impl TabPanel {
cx.notify();
}
/// Add panel to try to split
pub fn add_panel_at(
&mut self,
panel: Arc<dyn PanelView>,
placement: Placement,
size: Option<Pixels>,
cx: &mut ViewContext<Self>,
) {
self.will_split_placement = Some(placement);
self.split_panel(panel, placement, size, cx);
cx.notify();
}
fn insert_panel_at(
&mut self,
panel: Arc<dyn PanelView>,
@ -401,7 +433,7 @@ impl TabPanel {
// Insert into new tabs
if let Some(placement) = self.will_split_placement {
self.split_panel(panel, placement, cx);
self.split_panel(panel, placement, None, cx);
} else {
if let Some(ix) = ix {
self.insert_panel_at(panel, ix, cx)
@ -418,11 +450,12 @@ impl TabPanel {
&self,
panel: Arc<dyn PanelView>,
placement: Placement,
size: Option<Pixels>,
cx: &mut ViewContext<Self>,
) {
let dock_area = self.dock_area.clone();
// wrap the panel in a TabPanel
let new_tab_panel = cx.new_view(|cx| Self::new(dock_area.clone(), cx));
let new_tab_panel = cx.new_view(|cx| Self::new(None, dock_area.clone(), cx));
new_tab_panel.update(cx, |view, cx| {
view.add_panel(panel, cx);
});
@ -436,11 +469,11 @@ impl TabPanel {
if parent_axis.is_vertical() && placement.is_vertical() {
stack_panel.update(cx, |view, cx| {
view.add_panel_at(new_tab_panel, ix, placement, dock_area.clone(), cx);
view.insert_panel_at(new_tab_panel, ix, placement, size, dock_area.clone(), cx);
});
} else if parent_axis.is_horizontal() && placement.is_horizontal() {
stack_panel.update(cx, |view, cx| {
view.add_panel_at(new_tab_panel, ix, placement, dock_area.clone(), cx);
view.insert_panel_at(new_tab_panel, ix, placement, size, dock_area.clone(), cx);
});
} else {
// 1. Create new StackPanel with new axis
@ -466,12 +499,12 @@ impl TabPanel {
new_stack_panel.update(cx, |view, cx| match placement {
Placement::Left | Placement::Top => {
view.add_panel(new_tab_panel, None, dock_area.clone(), cx);
view.add_panel(new_tab_panel, size, dock_area.clone(), cx);
view.add_panel(tab_panel.clone(), None, dock_area.clone(), cx);
}
Placement::Right | Placement::Bottom => {
view.add_panel(tab_panel.clone(), None, dock_area.clone(), cx);
view.add_panel(new_tab_panel, None, dock_area.clone(), cx);
view.add_panel(new_tab_panel, size, dock_area.clone(), cx);
}
});

View file

@ -2,8 +2,8 @@ use std::rc::Rc;
use gpui::{
canvas, div, prelude::FluentBuilder, px, Along, AnyElement, AnyView, Axis, Bounds, Element,
EntityId, InteractiveElement as _, IntoElement, MouseMoveEvent, MouseUpEvent, ParentElement,
Pixels, Render, StatefulInteractiveElement, Style, Styled, View, ViewContext,
Entity, EntityId, InteractiveElement as _, IntoElement, MouseMoveEvent, MouseUpEvent,
ParentElement, Pixels, Render, StatefulInteractiveElement, Style, Styled, View, ViewContext,
VisualContext as _, WindowContext,
};
@ -89,26 +89,19 @@ impl ResizablePanelGroup {
self
}
/// When add a child panel to the group, resize other panels each into half of the remaining space.
fn default_panel_size(&self) -> Pixels {
let container_size = self.bounds.size.along(self.axis);
let each_size = container_size / (self.panels.len() + 1) as f32;
each_size.round()
}
pub fn add_child(&mut self, panel: ResizablePanel, cx: &mut ViewContext<Self>) {
let mut panel = panel;
panel.axis = self.axis;
panel.size = self.default_panel_size();
self.sizes.push(panel.size);
panel.group = Some(cx.view().clone());
self.sizes.push(panel.size.unwrap_or_default());
self.panels.push(cx.new_view(|_| panel));
}
pub fn insert_child(&mut self, panel: ResizablePanel, ix: usize, cx: &mut ViewContext<Self>) {
let mut panel = panel;
panel.axis = self.axis;
panel.size = self.default_panel_size();
self.sizes.insert(ix, panel.size);
panel.group = Some(cx.view().clone());
self.sizes.insert(ix, panel.size.unwrap_or_default());
self.panels.insert(ix, cx.new_view(|_| panel));
cx.notify()
}
@ -122,8 +115,8 @@ impl ResizablePanelGroup {
) {
let mut panel = panel;
panel.axis = self.axis;
panel.size = self.default_panel_size();
self.sizes[ix] = panel.size;
panel.group = Some(cx.view().clone());
self.sizes[ix] = panel.size.unwrap_or_default();
self.panels[ix] = cx.new_view(|_| panel);
cx.notify()
}
@ -142,7 +135,7 @@ impl ResizablePanelGroup {
fn render_resize_handle(&self, ix: usize, cx: &mut ViewContext<Self>) -> impl IntoElement {
let axis = self.axis;
let neg_offset = -HANDLE_PADDING + px(1.);
let neg_offset = -HANDLE_PADDING;
let view = cx.view().clone();
div()
@ -153,14 +146,14 @@ impl ResizablePanelGroup {
.when(self.axis.is_horizontal(), |this| {
this.cursor_col_resize()
.top_0()
.right(neg_offset)
.left(neg_offset)
.h_full()
.w(px(1.))
.px(HANDLE_PADDING)
})
.when(self.axis.is_vertical(), |this| {
this.cursor_row_resize()
.bottom(neg_offset)
.top(neg_offset)
.left_0()
.w_full()
.h(px(1.))
@ -250,7 +243,9 @@ impl ResizablePanelGroup {
self.sizes = new_sizes;
for (i, panel) in self.panels.iter().enumerate() {
let size = self.sizes[i];
panel.update(cx, |this, _| this.size = size);
if size > px(0.) {
panel.update(cx, |this, _| this.changed_size = Some(size));
}
}
}
}
@ -267,8 +262,8 @@ impl Render for ResizablePanelGroup {
container
.size_full()
.children(self.panels.iter().enumerate().map(|(ix, panel)| {
if ix < self.panels.len() - 1 {
let handle = self.render_resize_handle(ix, cx);
if ix > 0 {
let handle = self.render_resize_handle(ix - 1, cx);
panel.update(cx, |view, _| {
view.resize_handle = Some(handle.into_any_element())
});
@ -292,7 +287,9 @@ impl Render for ResizablePanelGroup {
}
pub struct ResizablePanel {
size: Pixels,
group: Option<View<ResizablePanelGroup>>,
size: Option<Pixels>,
changed_size: Option<Pixels>,
axis: Axis,
content_builder: Option<Rc<dyn Fn(&mut WindowContext) -> AnyElement>>,
content_view: Option<AnyView>,
@ -304,7 +301,9 @@ pub struct ResizablePanel {
impl ResizablePanel {
pub(super) fn new() -> Self {
Self {
size: PANEL_MIN_SIZE,
group: None,
size: None,
changed_size: None,
axis: Axis::Horizontal,
content_builder: None,
content_view: None,
@ -327,9 +326,29 @@ impl ResizablePanel {
}
pub fn size(mut self, size: Pixels) -> Self {
self.size = size;
self.size = Some(size);
self
}
/// Save the real panel size, and update group sizes
fn update_size(&mut self, bounds: Bounds<Pixels>, cx: &mut ViewContext<Self>) {
let new_size = bounds.size.along(self.axis);
self.bounds = bounds;
let panel_view = cx.view().clone();
if let Some(group) = self.group.as_ref() {
group.update(cx, |view, _| {
if let Some(ix) = view
.panels
.iter()
.position(|v| v.entity_id() == panel_view.entity_id())
{
view.sizes[ix] = new_size;
}
})
}
cx.notify();
}
}
impl FluentBuilder for ResizablePanel {}
@ -337,24 +356,27 @@ impl FluentBuilder for ResizablePanel {}
impl Render for ResizablePanel {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let view = cx.view().clone();
let axis = self.axis;
let size = self.size.max(PANEL_MIN_SIZE);
div()
.flex()
.flex_grow()
.size_full()
.relative()
.overflow_hidden()
.when(self.axis.is_vertical(), |this| this.w_full().h(size))
.when(self.axis.is_horizontal(), |this| this.h_full().w(size))
.when(self.size.is_none(), |this| this.flex_shrink())
.when(self.axis.is_vertical(), |this| this.min_h(PANEL_MIN_SIZE))
.when(self.axis.is_horizontal(), |this| this.min_w(PANEL_MIN_SIZE))
.when_some(self.size, |this, size| {
this.flex_shrink_0()
.when(self.axis.is_vertical(), |this| this.h(size))
.when(self.axis.is_horizontal(), |this| this.w(size))
})
.when_some(self.changed_size, |this, size| {
this.when(self.axis.is_vertical(), |this| this.h(size))
.when(self.axis.is_horizontal(), |this| this.w(size))
})
.child({
canvas(
move |bounds, cx| {
view.update(cx, |r, _| {
r.size = bounds.size.along(axis);
r.bounds = bounds;
})
},
move |bounds, cx| view.update(cx, |r, cx| r.update_size(bounds, cx)),
|_, _, _| {},
)
.absolute()