feat(shuma-shell): divisores arrastrables entre los paneles
Cada panel lateral tiene ahora un divisor de 5px que se arrastra para redimensionarlo (cursor resize, resaltado al hover). El arrastre se sigue a nivel de ventana —on_mouse_move/up en la raíz— así que no se pierde aunque el cursor salga del divisor. Ancho acotado 130–420px; los divisores desaparecen con el panel colapsado. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -20,9 +20,10 @@ use std::panic;
|
|||||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, point, prelude::*, px, App, Bounds, Context, Element, ElementId, FocusHandle,
|
div, point, prelude::*, px, App, Bounds, Context, CursorStyle, Element, ElementId, FocusHandle,
|
||||||
GlobalElementId, Hsla, InspectorElementId, IntoElement, KeyDownEvent, LayoutId, PathBuilder,
|
GlobalElementId, Hsla, InspectorElementId, IntoElement, KeyDownEvent, LayoutId, MouseButton,
|
||||||
Pixels, Render, SharedString, Style, Window,
|
MouseDownEvent, MouseMoveEvent, MouseUpEvent, PathBuilder, Pixels, Render, SharedString, Style,
|
||||||
|
Window,
|
||||||
};
|
};
|
||||||
use nahual_launcher::launch_app;
|
use nahual_launcher::launch_app;
|
||||||
use nahual_theme::Theme;
|
use nahual_theme::Theme;
|
||||||
@@ -201,6 +202,22 @@ impl Element for CurveElement {
|
|||||||
// El shell.
|
// El shell.
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
|
|
||||||
|
/// Qué panel lateral está redimensionando un drag activo.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum Side {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Estado de un arrastre de divisor en curso.
|
||||||
|
struct Drag {
|
||||||
|
side: Side,
|
||||||
|
/// Posición X del cursor al iniciar el arrastre.
|
||||||
|
start_x: f32,
|
||||||
|
/// Ancho del panel al iniciar el arrastre.
|
||||||
|
start_w: f32,
|
||||||
|
}
|
||||||
|
|
||||||
struct Shell {
|
struct Shell {
|
||||||
line: LineState,
|
line: LineState,
|
||||||
/// La sesión de trabajo: cwd, historial y grupos.
|
/// La sesión de trabajo: cwd, historial y grupos.
|
||||||
@@ -215,6 +232,11 @@ struct Shell {
|
|||||||
snapshot: Snapshot,
|
snapshot: Snapshot,
|
||||||
left_collapsed: bool,
|
left_collapsed: bool,
|
||||||
right_collapsed: bool,
|
right_collapsed: bool,
|
||||||
|
/// Anchos de los paneles laterales (los divisores los ajustan).
|
||||||
|
left_width: f32,
|
||||||
|
right_width: f32,
|
||||||
|
/// Arrastre de divisor en curso, si lo hay.
|
||||||
|
drag: Option<Drag>,
|
||||||
focus: FocusHandle,
|
focus: FocusHandle,
|
||||||
focused_once: bool,
|
focused_once: bool,
|
||||||
}
|
}
|
||||||
@@ -248,6 +270,9 @@ impl Shell {
|
|||||||
},
|
},
|
||||||
left_collapsed: false,
|
left_collapsed: false,
|
||||||
right_collapsed: false,
|
right_collapsed: false,
|
||||||
|
left_width: 176.0,
|
||||||
|
right_width: 188.0,
|
||||||
|
drag: None,
|
||||||
focus: cx.focus_handle(),
|
focus: cx.focus_handle(),
|
||||||
focused_once: false,
|
focused_once: false,
|
||||||
};
|
};
|
||||||
@@ -687,7 +712,7 @@ impl Render for Shell {
|
|||||||
.collect();
|
.collect();
|
||||||
div()
|
div()
|
||||||
.id("run-panel")
|
.id("run-panel")
|
||||||
.w(px(176.))
|
.w(px(self.left_width))
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.gap(px(6.))
|
.gap(px(6.))
|
||||||
@@ -796,7 +821,7 @@ impl Render for Shell {
|
|||||||
|
|
||||||
div()
|
div()
|
||||||
.id("sens-panel")
|
.id("sens-panel")
|
||||||
.w(px(184.))
|
.w(px(self.right_width))
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.gap(px(10.))
|
.gap(px(10.))
|
||||||
@@ -949,6 +974,40 @@ impl Render for Shell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Divisores arrastrables ---
|
||||||
|
let divider = |side: Side, cx: &mut Context<Self>| {
|
||||||
|
div()
|
||||||
|
.w(px(5.))
|
||||||
|
.bg(node_bg)
|
||||||
|
.cursor(CursorStyle::ResizeLeftRight)
|
||||||
|
.hover(|s| s.bg(accent))
|
||||||
|
.on_mouse_down(
|
||||||
|
MouseButton::Left,
|
||||||
|
cx.listener(move |shell, ev: &MouseDownEvent, _w, cx| {
|
||||||
|
let start_w = match side {
|
||||||
|
Side::Left => shell.left_width,
|
||||||
|
Side::Right => shell.right_width,
|
||||||
|
};
|
||||||
|
shell.drag = Some(Drag {
|
||||||
|
side,
|
||||||
|
start_x: ev.position.x.into(),
|
||||||
|
start_w,
|
||||||
|
});
|
||||||
|
cx.notify();
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut middle = div().flex().flex_row().flex_1().overflow_hidden().child(left);
|
||||||
|
if !self.left_collapsed {
|
||||||
|
middle = middle.child(divider(Side::Left, cx));
|
||||||
|
}
|
||||||
|
middle = middle.child(canvas);
|
||||||
|
if !self.right_collapsed {
|
||||||
|
middle = middle.child(divider(Side::Right, cx));
|
||||||
|
}
|
||||||
|
middle = middle.child(right);
|
||||||
|
|
||||||
// --- Composición ---
|
// --- Composición ---
|
||||||
div()
|
div()
|
||||||
.size_full()
|
.size_full()
|
||||||
@@ -959,17 +1018,33 @@ impl Render for Shell {
|
|||||||
.track_focus(&self.focus)
|
.track_focus(&self.focus)
|
||||||
.key_context("ShumaShell")
|
.key_context("ShumaShell")
|
||||||
.on_key_down(cx.listener(Self::handle_key))
|
.on_key_down(cx.listener(Self::handle_key))
|
||||||
.child(status)
|
.on_mouse_move(cx.listener(|shell, ev: &MouseMoveEvent, _w, cx| {
|
||||||
.child(
|
if let Some(drag) = &shell.drag {
|
||||||
div()
|
let cur: f32 = ev.position.x.into();
|
||||||
.flex()
|
let delta = cur - drag.start_x;
|
||||||
.flex_row()
|
match drag.side {
|
||||||
.flex_1()
|
// El panel izquierdo crece al arrastrar a la derecha.
|
||||||
.overflow_hidden()
|
Side::Left => {
|
||||||
.child(left)
|
shell.left_width = (drag.start_w + delta).clamp(130.0, 420.0)
|
||||||
.child(canvas)
|
}
|
||||||
.child(right),
|
// El derecho crece al arrastrar a la izquierda.
|
||||||
|
Side::Right => {
|
||||||
|
shell.right_width = (drag.start_w - delta).clamp(130.0, 420.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.on_mouse_up(
|
||||||
|
MouseButton::Left,
|
||||||
|
cx.listener(|shell, _ev: &MouseUpEvent, _w, cx| {
|
||||||
|
if shell.drag.take().is_some() {
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
.child(status)
|
||||||
|
.child(middle)
|
||||||
.child(prompt)
|
.child(prompt)
|
||||||
.children(popup_layer)
|
.children(popup_layer)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user