debugger: Make debug panes zoomable (#29365)
- [x] Buttons - [x] Make it keyboard-driven Co-authored-by: Anthony <anthony@zed.dev> Release Notes: - N/A --------- Co-authored-by: Anthony Eid <hello@anthonyeid.me>
This commit is contained in:
parent
a5405fcbd7
commit
65401d6d7b
2 changed files with 168 additions and 100 deletions
|
@ -29,9 +29,10 @@ use settings::Settings;
|
|||
use stack_frame_list::StackFrameList;
|
||||
use terminal_view::TerminalView;
|
||||
use ui::{
|
||||
ActiveTheme, AnyElement, App, Context, ContextMenu, DropdownMenu, FluentBuilder,
|
||||
InteractiveElement, IntoElement, Label, LabelCommon as _, ParentElement, Render, SharedString,
|
||||
StatefulInteractiveElement, Styled, Tab, Window, div, h_flex, v_flex,
|
||||
ActiveTheme, AnyElement, App, ButtonCommon as _, Clickable as _, Context, ContextMenu,
|
||||
DropdownMenu, FluentBuilder, IconButton, IconName, IconSize, InteractiveElement, IntoElement,
|
||||
Label, LabelCommon as _, ParentElement, Render, SharedString, StatefulInteractiveElement,
|
||||
Styled, Tab, Tooltip, Window, div, h_flex, v_flex,
|
||||
};
|
||||
use util::ResultExt;
|
||||
use variable_list::VariableList;
|
||||
|
@ -62,8 +63,16 @@ pub struct RunningState {
|
|||
|
||||
impl Render for RunningState {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let zoomed_pane = self
|
||||
.panes
|
||||
.panes()
|
||||
.into_iter()
|
||||
.find(|pane| pane.read(cx).is_zoomed());
|
||||
|
||||
let active = self.panes.panes().into_iter().next();
|
||||
let x = if let Some(active) = active {
|
||||
let x = if let Some(ref zoomed_pane) = zoomed_pane {
|
||||
zoomed_pane.update(cx, |pane, cx| pane.render(window, cx).into_any_element())
|
||||
} else if let Some(active) = active {
|
||||
self.panes
|
||||
.render(
|
||||
None,
|
||||
|
@ -257,109 +266,148 @@ pub(crate) fn new_debugger_pane(
|
|||
window,
|
||||
cx,
|
||||
);
|
||||
pane.set_can_split(Some(Arc::new(move |pane, dragged_item, _window, cx| {
|
||||
if let Some(tab) = dragged_item.downcast_ref::<DraggedTab>() {
|
||||
let is_current_pane = tab.pane == cx.entity();
|
||||
let Some(can_drag_away) = weak_running
|
||||
.update(cx, |running_state, _| {
|
||||
let current_panes = running_state.panes.panes();
|
||||
!current_panes.contains(&&tab.pane)
|
||||
|| current_panes.len() > 1
|
||||
|| (!is_current_pane || pane.items_len() > 1)
|
||||
})
|
||||
.ok()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
if can_drag_away {
|
||||
let item = if is_current_pane {
|
||||
pane.item_for_index(tab.ix)
|
||||
} else {
|
||||
tab.pane.read(cx).item_for_index(tab.ix)
|
||||
let focus_handle = pane.focus_handle(cx);
|
||||
pane.set_can_split(Some(Arc::new({
|
||||
let weak_running = weak_running.clone();
|
||||
move |pane, dragged_item, _window, cx| {
|
||||
if let Some(tab) = dragged_item.downcast_ref::<DraggedTab>() {
|
||||
let is_current_pane = tab.pane == cx.entity();
|
||||
let Some(can_drag_away) = weak_running
|
||||
.update(cx, |running_state, _| {
|
||||
let current_panes = running_state.panes.panes();
|
||||
!current_panes.contains(&&tab.pane)
|
||||
|| current_panes.len() > 1
|
||||
|| (!is_current_pane || pane.items_len() > 1)
|
||||
})
|
||||
.ok()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
if let Some(item) = item {
|
||||
return item.downcast::<SubView>().is_some();
|
||||
if can_drag_away {
|
||||
let item = if is_current_pane {
|
||||
pane.item_for_index(tab.ix)
|
||||
} else {
|
||||
tab.pane.read(cx).item_for_index(tab.ix)
|
||||
};
|
||||
if let Some(item) = item {
|
||||
return item.downcast::<SubView>().is_some();
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
false
|
||||
})));
|
||||
pane.display_nav_history_buttons(None);
|
||||
pane.set_custom_drop_handle(cx, custom_drop_handle);
|
||||
pane.set_should_display_tab_bar(|_, _| true);
|
||||
pane.set_render_tab_bar_buttons(cx, |_, _, _| (None, None));
|
||||
pane.set_render_tab_bar(cx, |pane, window, cx| {
|
||||
let active_pane_item = pane.active_item();
|
||||
h_flex()
|
||||
.w_full()
|
||||
.px_2()
|
||||
.gap_1()
|
||||
.h(Tab::container_height(cx))
|
||||
.drag_over::<DraggedTab>(|bar, _, _, cx| {
|
||||
bar.bg(cx.theme().colors().drop_target_background)
|
||||
})
|
||||
.on_drop(
|
||||
cx.listener(move |this, dragged_tab: &DraggedTab, window, cx| {
|
||||
this.drag_split_direction = None;
|
||||
this.handle_tab_drop(dragged_tab, this.items_len(), window, cx)
|
||||
}),
|
||||
)
|
||||
.bg(cx.theme().colors().tab_bar_background)
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.children(pane.items().enumerate().map(|(ix, item)| {
|
||||
let selected = active_pane_item
|
||||
.as_ref()
|
||||
.map_or(false, |active| active.item_id() == item.item_id());
|
||||
let item_ = item.boxed_clone();
|
||||
div()
|
||||
.id(SharedString::from(format!(
|
||||
"debugger_tab_{}",
|
||||
item.item_id().as_u64()
|
||||
)))
|
||||
.p_1()
|
||||
.rounded_md()
|
||||
.cursor_pointer()
|
||||
.map(|this| {
|
||||
if selected {
|
||||
this.bg(cx.theme().colors().tab_active_background)
|
||||
pane.set_render_tab_bar(cx, {
|
||||
move |pane, window, cx| {
|
||||
let active_pane_item = pane.active_item();
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.bg(cx.theme().colors().tab_bar_background)
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.track_focus(&focus_handle)
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.px_2()
|
||||
.gap_1()
|
||||
.h(Tab::container_height(cx))
|
||||
.drag_over::<DraggedTab>(|bar, _, _, cx| {
|
||||
bar.bg(cx.theme().colors().drop_target_background)
|
||||
})
|
||||
.on_drop(cx.listener(
|
||||
move |this, dragged_tab: &DraggedTab, window, cx| {
|
||||
this.drag_split_direction = None;
|
||||
this.handle_tab_drop(dragged_tab, this.items_len(), window, cx)
|
||||
},
|
||||
))
|
||||
.children(pane.items().enumerate().map(|(ix, item)| {
|
||||
let selected = active_pane_item
|
||||
.as_ref()
|
||||
.map_or(false, |active| active.item_id() == item.item_id());
|
||||
let item_ = item.boxed_clone();
|
||||
div()
|
||||
.id(SharedString::from(format!(
|
||||
"debugger_tab_{}",
|
||||
item.item_id().as_u64()
|
||||
)))
|
||||
.p_1()
|
||||
.rounded_md()
|
||||
.cursor_pointer()
|
||||
.map(|this| {
|
||||
if selected {
|
||||
this.bg(cx.theme().colors().tab_active_background)
|
||||
} else {
|
||||
let hover_color = cx.theme().colors().element_hover;
|
||||
this.hover(|style| style.bg(hover_color))
|
||||
}
|
||||
})
|
||||
.on_click(cx.listener(move |this, _, window, cx| {
|
||||
let index = this.index_for_item(&*item_);
|
||||
if let Some(index) = index {
|
||||
this.activate_item(index, true, true, window, cx);
|
||||
}
|
||||
}))
|
||||
.child(item.tab_content(
|
||||
TabContentParams {
|
||||
selected,
|
||||
..Default::default()
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
.on_drop(cx.listener(
|
||||
move |this, dragged_tab: &DraggedTab, window, cx| {
|
||||
this.drag_split_direction = None;
|
||||
this.handle_tab_drop(dragged_tab, ix, window, cx)
|
||||
},
|
||||
))
|
||||
.on_drag(
|
||||
DraggedTab {
|
||||
item: item.boxed_clone(),
|
||||
pane: cx.entity().clone(),
|
||||
detail: 0,
|
||||
is_active: selected,
|
||||
ix,
|
||||
},
|
||||
|tab, _, _, cx| cx.new(|_| tab.clone()),
|
||||
)
|
||||
})),
|
||||
)
|
||||
.child({
|
||||
let zoomed = pane.is_zoomed();
|
||||
IconButton::new(
|
||||
"debug-toggle-zoom",
|
||||
if zoomed {
|
||||
IconName::Minimize
|
||||
} else {
|
||||
let hover_color = cx.theme().colors().element_hover;
|
||||
this.hover(|style| style.bg(hover_color))
|
||||
IconName::Maximize
|
||||
},
|
||||
)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(cx.listener(move |pane, _, window, cx| {
|
||||
pane.toggle_zoom(&workspace::ToggleZoom, window, cx);
|
||||
}))
|
||||
.tooltip({
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |window, cx| {
|
||||
let zoomed_text = if zoomed { "Zoom Out" } else { "Zoom In" };
|
||||
Tooltip::for_action_in(
|
||||
zoomed_text,
|
||||
&workspace::ToggleZoom,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click(cx.listener(move |this, _, window, cx| {
|
||||
let index = this.index_for_item(&*item_);
|
||||
if let Some(index) = index {
|
||||
this.activate_item(index, true, true, window, cx);
|
||||
}
|
||||
}))
|
||||
.child(item.tab_content(
|
||||
TabContentParams {
|
||||
selected,
|
||||
..Default::default()
|
||||
},
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
.on_drop(
|
||||
cx.listener(move |this, dragged_tab: &DraggedTab, window, cx| {
|
||||
this.drag_split_direction = None;
|
||||
this.handle_tab_drop(dragged_tab, ix, window, cx)
|
||||
}),
|
||||
)
|
||||
.on_drag(
|
||||
DraggedTab {
|
||||
item: item.boxed_clone(),
|
||||
pane: cx.entity().clone(),
|
||||
detail: 0,
|
||||
is_active: selected,
|
||||
ix,
|
||||
},
|
||||
|tab, _, _, cx| cx.new(|_| tab.clone()),
|
||||
)
|
||||
}))
|
||||
.into_any_element()
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
});
|
||||
pane
|
||||
});
|
||||
|
@ -730,10 +778,25 @@ impl RunningState {
|
|||
cx: &mut Context<RunningState>,
|
||||
) {
|
||||
this.serialize_layout(window, cx);
|
||||
if let Event::Remove { .. } = event {
|
||||
let _did_find_pane = this.panes.remove(&source_pane).is_ok();
|
||||
debug_assert!(_did_find_pane);
|
||||
cx.notify();
|
||||
match event {
|
||||
Event::Remove { .. } => {
|
||||
let _did_find_pane = this.panes.remove(&source_pane).is_ok();
|
||||
debug_assert!(_did_find_pane);
|
||||
cx.notify();
|
||||
}
|
||||
Event::ZoomIn => {
|
||||
source_pane.update(cx, |pane, cx| {
|
||||
pane.set_zoomed(true, cx);
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
Event::ZoomOut => {
|
||||
source_pane.update(cx, |pane, cx| {
|
||||
pane.set_zoomed(false, cx);
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue