forked from reya/gpui-native-kit
chore: reduce component overhead (#3)
Reviewed-on: reya/gpui-native-kit#3
This commit is contained in:
+40
-22
@@ -192,10 +192,8 @@ impl DockState {
|
||||
}
|
||||
|
||||
/// Registers a panel descriptor, replacing any earlier one with its id.
|
||||
pub fn register_panel(&mut self, panel: PanelDescriptor, cx: &mut Context<Self>) {
|
||||
if self.panels.insert(panel.id.clone(), panel).is_none() {
|
||||
cx.notify();
|
||||
}
|
||||
pub fn register_panel(&mut self, panel: PanelDescriptor) {
|
||||
self.panels.insert(panel.id.clone(), panel);
|
||||
}
|
||||
|
||||
/// Opens a panel into the active pane and activates its tab.
|
||||
@@ -330,6 +328,17 @@ impl DockState {
|
||||
};
|
||||
|
||||
if changed {
|
||||
// The kept tab may have no right view, show the first remaining one.
|
||||
self.right_view = self
|
||||
.locate(id)
|
||||
.and_then(|(pane_key, _)| self.pane(pane_key))
|
||||
.and_then(|pane| {
|
||||
pane.active.as_deref().and_then(|active| {
|
||||
pane.tabs.iter().find(|tab| tab.panel.as_ref() == active)
|
||||
})
|
||||
})
|
||||
.and_then(|tab| tab.right_view.clone())
|
||||
.or_else(|| first_right_view(&self.root));
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
@@ -354,10 +363,14 @@ impl DockState {
|
||||
}
|
||||
|
||||
self.detach_pane(pane_key);
|
||||
|
||||
if self.active_pane == pane_key {
|
||||
self.active_pane = self.first_pane_key().unwrap_or(0);
|
||||
}
|
||||
|
||||
// The closed pane's views are gone; show the first remaining one.
|
||||
self.right_view = first_right_view(&self.root);
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -526,6 +539,19 @@ impl DockState {
|
||||
}
|
||||
}
|
||||
|
||||
fn pane(&self, key: u64) -> Option<&Pane> {
|
||||
Self::pane_in(&self.root, key)
|
||||
}
|
||||
|
||||
fn pane_in(node: &Node, key: u64) -> Option<&Pane> {
|
||||
match node {
|
||||
Node::Pane(pane) => (pane.key == key).then_some(pane),
|
||||
Node::Split(split) => {
|
||||
Self::pane_in(&split.a, key).or_else(|| Self::pane_in(&split.b, key))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pane_mut(&mut self, key: u64) -> Option<&mut Pane> {
|
||||
Self::pane_mut_in(&mut self.root, key)
|
||||
}
|
||||
@@ -969,15 +995,10 @@ fn render_pane(
|
||||
.map(|tab| tab.view.clone());
|
||||
|
||||
// The cache keeps an un-notified panel from re-rendering on dock frames.
|
||||
let content_id = SharedString::from(format!("{dock_id}-content-{}", pane.key));
|
||||
let content = div()
|
||||
.id(SharedString::from(format!(
|
||||
"{dock_id}-content-{}",
|
||||
pane.key
|
||||
)))
|
||||
.debug_selector({
|
||||
let id = SharedString::from(format!("{dock_id}-content-{}", pane.key));
|
||||
move || id.to_string()
|
||||
})
|
||||
.id(content_id.clone())
|
||||
.debug_selector(move || content_id.to_string())
|
||||
.flex_1()
|
||||
.min_h_0()
|
||||
.overflow_hidden()
|
||||
@@ -999,6 +1020,7 @@ fn render_pane(
|
||||
let pane = pane.clone();
|
||||
let titlebar = *titlebar;
|
||||
let padding_x = metrics.dense_padding_x;
|
||||
|
||||
move |window, cx| {
|
||||
let selected = pane
|
||||
.active
|
||||
@@ -1016,7 +1038,7 @@ fn render_pane(
|
||||
move |_, _| {
|
||||
tabs.iter()
|
||||
.map(|tab| {
|
||||
let mut entry = Tab::new(tab.title.clone());
|
||||
let mut entry = Tab::new(tab.panel.clone(), tab.title.clone());
|
||||
if let Some(role) = tab.icon {
|
||||
entry = entry.icon(role);
|
||||
}
|
||||
@@ -1065,21 +1087,17 @@ fn render_pane(
|
||||
}
|
||||
});
|
||||
|
||||
let bar_id = SharedString::from(format!("{dock_id}-pane-{}-bar", pane.key));
|
||||
|
||||
h_flex()
|
||||
.id(SharedString::from(format!(
|
||||
"{dock_id}-pane-{}-bar",
|
||||
pane.key
|
||||
)))
|
||||
.debug_selector({
|
||||
let id = SharedString::from(format!("{dock_id}-pane-{}-bar", pane.key));
|
||||
move || id.to_string()
|
||||
})
|
||||
.id(bar_id.clone())
|
||||
.debug_selector(move || bar_id.to_string())
|
||||
.flex_none()
|
||||
.w_full()
|
||||
.h(titlebar.height)
|
||||
.items_center()
|
||||
.pl(if titlebar.traffic_light_pad {
|
||||
px(TRAFFIC_LIGHT_PAD)
|
||||
TRAFFIC_LIGHT_PAD
|
||||
} else {
|
||||
padding_x
|
||||
})
|
||||
|
||||
@@ -10,11 +10,11 @@ use gpui::{
|
||||
SharedString, StatefulInteractiveElement as _, Styled as _, Window, canvas, deferred, div,
|
||||
fill, point, px,
|
||||
};
|
||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||
use gpui_native_theme::{ActiveTheme as _, RESIZE_KEYBOARD_STEP, Size};
|
||||
use gpui_native_utils::{focus_handle, focus_ring};
|
||||
|
||||
/// How far one keyboard arrow step moves the seam.
|
||||
const KEYBOARD_STEP: f32 = 32.0;
|
||||
const KEYBOARD_STEP: Pixels = RESIZE_KEYBOARD_STEP;
|
||||
|
||||
type ResizeHandler = Rc<dyn Fn(Pixels, &mut Window, &mut App)>;
|
||||
type ResetHandler = Rc<dyn Fn(&mut Window, &mut App)>;
|
||||
@@ -180,10 +180,10 @@ impl RenderOnce for ResizeHandle {
|
||||
let on_resize = self.on_resize.clone();
|
||||
move |event: &KeyDownEvent, window, cx| {
|
||||
let delta = match (axis, event.keystroke.key.as_str()) {
|
||||
(Axis::Horizontal, "left") => Some(px(-KEYBOARD_STEP)),
|
||||
(Axis::Horizontal, "right") => Some(px(KEYBOARD_STEP)),
|
||||
(Axis::Vertical, "up") => Some(px(-KEYBOARD_STEP)),
|
||||
(Axis::Vertical, "down") => Some(px(KEYBOARD_STEP)),
|
||||
(Axis::Horizontal, "left") => Some(-KEYBOARD_STEP),
|
||||
(Axis::Horizontal, "right") => Some(KEYBOARD_STEP),
|
||||
(Axis::Vertical, "up") => Some(-KEYBOARD_STEP),
|
||||
(Axis::Vertical, "down") => Some(KEYBOARD_STEP),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(delta) = delta {
|
||||
@@ -255,6 +255,14 @@ impl RenderOnce for ResizeHandle {
|
||||
return;
|
||||
}
|
||||
|
||||
if event.pressed_button != Some(MouseButton::Left) {
|
||||
drag.update(cx, |drag, cx| {
|
||||
drag.dragging = false;
|
||||
cx.notify();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let delta = match axis {
|
||||
Axis::Horizontal => event.position.x - last.x,
|
||||
Axis::Vertical => event.position.y - last.y,
|
||||
|
||||
+20
-40
@@ -4,8 +4,7 @@
|
||||
use gpui::prelude::FluentBuilder as _;
|
||||
use gpui::{
|
||||
AnyElement, App, AppContext as _, Context, ElementId, Entity, InteractiveElement as _,
|
||||
IntoElement, ParentElement as _, Pixels, RenderOnce, SharedString, Styled as _, Window, div,
|
||||
px,
|
||||
IntoElement, ParentElement as _, Pixels, RenderOnce, Styled as _, Window, div, px,
|
||||
};
|
||||
use gpui_native_core::button::{Button, ButtonVariant};
|
||||
use gpui_native_core::common::{Sizable as _, h_flex, v_flex};
|
||||
@@ -108,20 +107,6 @@ impl Side {
|
||||
Side::Right => "right",
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_icon(self) -> IconRole {
|
||||
match self {
|
||||
Side::Left => IconRole::PanelLeft,
|
||||
Side::Right => IconRole::PanelRight,
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_label(self) -> SharedString {
|
||||
match self {
|
||||
Side::Left => SharedString::from("Hide left sidebar"),
|
||||
Side::Right => SharedString::from("Hide right sidebar"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The application shell.
|
||||
@@ -204,6 +189,7 @@ impl RenderOnce for Shell {
|
||||
let width = state.read(cx).left_sidebar_width();
|
||||
layout
|
||||
.child(sidebar_region(
|
||||
(id.clone(), "left"),
|
||||
width,
|
||||
left.take()
|
||||
.map(|sidebar| sidebar.into_any_element())
|
||||
@@ -242,6 +228,7 @@ impl RenderOnce for Shell {
|
||||
}
|
||||
}))
|
||||
.child(sidebar_region(
|
||||
(id.clone(), "right"),
|
||||
width,
|
||||
right.take().unwrap_or_else(|| div().into_any_element()),
|
||||
Side::Right,
|
||||
@@ -262,6 +249,7 @@ impl RenderOnce for Shell {
|
||||
/// One sidebar: a header strip with the hide toggle over the app's content.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn sidebar_region(
|
||||
id: impl Into<ElementId>,
|
||||
width: Pixels,
|
||||
content: AnyElement,
|
||||
side: Side,
|
||||
@@ -272,6 +260,7 @@ fn sidebar_region(
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyElement {
|
||||
let id = id.into();
|
||||
let theme = cx.theme();
|
||||
let background = theme.colors.background;
|
||||
let strip_background = match (side, material) {
|
||||
@@ -280,7 +269,7 @@ fn sidebar_region(
|
||||
};
|
||||
|
||||
let strip = h_flex()
|
||||
.id(SharedString::from(format!("sidebar-strip-{}", side.name())))
|
||||
.id((id.clone(), "strip"))
|
||||
.debug_selector(move || format!("sidebar-strip-{}", side.name()))
|
||||
.flex_none()
|
||||
.h(strip_height)
|
||||
@@ -288,40 +277,31 @@ fn sidebar_region(
|
||||
.items_center()
|
||||
.map(|strip| match side {
|
||||
Side::Left => strip.when(cfg!(target_os = "macos"), |strip| {
|
||||
strip.pl(px(TRAFFIC_LIGHT_PAD))
|
||||
strip.pl(TRAFFIC_LIGHT_PAD)
|
||||
}),
|
||||
Side::Right => strip,
|
||||
})
|
||||
.px(padding_x)
|
||||
.child(titlebar_strip(
|
||||
div()
|
||||
.id(SharedString::from(format!(
|
||||
"sidebar-strip-drag-{}",
|
||||
side.name()
|
||||
)))
|
||||
.flex_1()
|
||||
.self_stretch(),
|
||||
div().id((id.clone(), "strip-drag")).flex_1().self_stretch(),
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
.map(|strip| match side {
|
||||
Side::Left => strip.child(
|
||||
Button::new(SharedString::from(format!(
|
||||
"sidebar-toggle-{}",
|
||||
side.name()
|
||||
)))
|
||||
.icon(side.toggle_icon())
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.small()
|
||||
.accessibility_label(side.toggle_label())
|
||||
.on_click({
|
||||
let state = state.clone();
|
||||
move |_, _, cx| {
|
||||
state.update(cx, |state, cx| state.set_left_sidebar_visible(false, cx))
|
||||
}
|
||||
}),
|
||||
Button::new((id, "sidebar-toggle"))
|
||||
.icon(IconRole::PanelLeft)
|
||||
.variant(ButtonVariant::Ghost)
|
||||
.small()
|
||||
.accessibility_label("Hide left sidebar")
|
||||
.on_click({
|
||||
let state = state.clone();
|
||||
move |_, _, cx| {
|
||||
state.update(cx, |state, cx| state.set_left_sidebar_visible(false, cx))
|
||||
}
|
||||
}),
|
||||
),
|
||||
Side::Right => strip.child(WindowControls::new("sidebar-window-controls")),
|
||||
Side::Right => strip.child(WindowControls::new((id, "window-controls"))),
|
||||
});
|
||||
|
||||
v_flex()
|
||||
|
||||
@@ -23,13 +23,9 @@ use gpui::Decorations;
|
||||
use gpui::{SharedString, px, rgba};
|
||||
|
||||
/// macOS traffic-lights zone: the pad a leading titlebar strip reserves.
|
||||
pub const TRAFFIC_LIGHT_PAD: f32 = 78.0;
|
||||
/// The width of a Windows caption button.
|
||||
pub use gpui_native_theme::TRAFFIC_LIGHT_PAD;
|
||||
#[cfg(target_os = "windows")]
|
||||
const CAPTION_WIDTH: f32 = 36.0;
|
||||
/// Windows paints its caption close hover red in every theme.
|
||||
#[cfg(target_os = "windows")]
|
||||
const WINDOWS_CLOSE_HOVER: (f32, f32, f32) = (232.0 / 255.0, 17.0 / 255.0, 32.0 / 255.0);
|
||||
use gpui_native_theme::{WINDOWS_CAPTION_WIDTH as CAPTION_WIDTH, WINDOWS_CLOSE_HOVER_HEX};
|
||||
|
||||
/// The window controls for the trailing end of a titlebar strip.
|
||||
#[derive(IntoElement)]
|
||||
@@ -150,7 +146,7 @@ impl RenderOnce for WindowControls {
|
||||
}
|
||||
|
||||
/// Double-click on a titlebar strip maximizes where the platform does not do it.
|
||||
pub fn maximize_on_double_click() -> impl Fn(&ClickEvent, &mut Window, &mut App) + 'static {
|
||||
fn maximize_on_double_click() -> impl Fn(&ClickEvent, &mut Window, &mut App) + 'static {
|
||||
move |event, window, _cx| {
|
||||
if event.click_count() != 2 {
|
||||
return;
|
||||
@@ -210,16 +206,9 @@ fn caption_button(
|
||||
) -> AnyElement {
|
||||
let theme = cx.theme();
|
||||
let glyph_size = theme.sizes.small.caption_size;
|
||||
let (close_r, close_g, close_b) = WINDOWS_CLOSE_HOVER;
|
||||
|
||||
let close_hover = rgba((
|
||||
(close_r * 255.0) as u8,
|
||||
(close_g * 255.0) as u8,
|
||||
(close_b * 255.0) as u8,
|
||||
0xff,
|
||||
));
|
||||
let hover = if close {
|
||||
close_hover
|
||||
rgba(WINDOWS_CLOSE_HOVER_HEX)
|
||||
} else {
|
||||
theme.colors.muted_hover
|
||||
};
|
||||
|
||||
@@ -7,12 +7,8 @@ use gpui::{
|
||||
IntoElement, MouseButton, ParentElement as _, Pixels, Point, ResizeEdge, Size, Styled as _,
|
||||
Tiling, Window, canvas, div, point, px, size,
|
||||
};
|
||||
use gpui_native_theme::ActiveTheme as _;
|
||||
use gpui_native_theme::{ActiveTheme as _, BORDER_WIDTH, WINDOW_RESIZE_INSET};
|
||||
|
||||
/// The width of the band inside the window's edge that resizes it.
|
||||
const RESIZE_INSET: f32 = 4.0;
|
||||
/// The hairline around the content.
|
||||
const BORDER_WIDTH: f32 = 1.0;
|
||||
/// How much of the shadow's strength survives losing window focus.
|
||||
const INACTIVE_SHADOW_OPACITY: f32 = 0.7;
|
||||
|
||||
@@ -28,7 +24,7 @@ pub fn frame(child: impl IntoElement, window: &mut Window, cx: &App) -> Div {
|
||||
tiling
|
||||
};
|
||||
|
||||
let inset = px(RESIZE_INSET);
|
||||
let inset = WINDOW_RESIZE_INSET;
|
||||
let border = cx.theme().colors.border;
|
||||
let radius = cx.theme().radius.overlay;
|
||||
|
||||
@@ -75,10 +71,10 @@ pub fn frame(child: impl IntoElement, window: &mut Window, cx: &App) -> Div {
|
||||
.size_full()
|
||||
.overflow_hidden()
|
||||
.border_color(border)
|
||||
.when(!tiling.top, |surface| surface.border_t(px(BORDER_WIDTH)))
|
||||
.when(!tiling.bottom, |surface| surface.border_b(px(BORDER_WIDTH)))
|
||||
.when(!tiling.left, |surface| surface.border_l(px(BORDER_WIDTH)))
|
||||
.when(!tiling.right, |surface| surface.border_r(px(BORDER_WIDTH)))
|
||||
.when(!tiling.top, |surface| surface.border_t(BORDER_WIDTH))
|
||||
.when(!tiling.bottom, |surface| surface.border_b(BORDER_WIDTH))
|
||||
.when(!tiling.left, |surface| surface.border_l(BORDER_WIDTH))
|
||||
.when(!tiling.right, |surface| surface.border_r(BORDER_WIDTH))
|
||||
.when(!(tiling.top || tiling.left), |surface| {
|
||||
surface.rounded_tl(radius)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user