diff --git a/crates/ui/src/components/data_table.rs b/crates/ui/src/components/data_table.rs index 43db2ebd3b9fa7053d16a5cd2f3aa2af0c662d2d..905c6290c1f2caea6b959bc453255854defc7a84 100644 --- a/crates/ui/src/components/data_table.rs +++ b/crates/ui/src/components/data_table.rs @@ -4,19 +4,18 @@ use crate::{ ActiveTheme as _, AnyElement, App, Button, ButtonCommon as _, ButtonStyle, Color, Component, ComponentScope, Context, Div, DraggedColumn, ElementId, FixedWidth as _, FluentBuilder as _, HeaderResizeInfo, Indicator, InteractiveElement, IntoElement, ParentElement, Pixels, - RESIZE_COLUMN_WIDTH, RESIZE_DIVIDER_WIDTH, RedistributableColumnsState, RegisterComponent, - RenderOnce, ScrollAxes, ScrollableHandle, Scrollbars, SharedString, StatefulInteractiveElement, - Styled, StyledExt as _, StyledTypography, TableResizeBehavior, Window, WithScrollbar, - bind_redistributable_columns, div, example_group_with_title, h_flex, px, + RESIZE_DIVIDER_WIDTH, RedistributableColumnsState, RegisterComponent, RenderOnce, ScrollAxes, + ScrollableHandle, Scrollbars, SharedString, StatefulInteractiveElement, Styled, StyledExt as _, + StyledTypography, TableResizeBehavior, Window, WithScrollbar, bind_redistributable_columns, + div, example_group_with_title, h_flex, px, render_column_resize_divider, render_redistributable_columns_resize_handles, single_example, table_row::{IntoTableRow as _, TableRow}, v_flex, }; use gpui::{ - AbsoluteLength, AppContext as _, ClickEvent, DefiniteLength, DragMoveEvent, Empty, Entity, - EntityId, FocusHandle, Length, ListHorizontalSizingBehavior, ListSizingBehavior, ListState, - Point, ScrollHandle, Stateful, UniformListScrollHandle, WeakEntity, list, transparent_black, - uniform_list, + AbsoluteLength, DefiniteLength, DragMoveEvent, Entity, EntityId, FocusHandle, Length, + ListHorizontalSizingBehavior, ListSizingBehavior, ListState, Point, ScrollHandle, Stateful, + UniformListScrollHandle, WeakEntity, list, transparent_black, uniform_list, }; pub mod table_row; @@ -707,71 +706,27 @@ fn render_resize_handles_resizable( } else { accumulated_px }; - let resize_behavior = Rc::clone(&resize_behavior); - let columns_state = columns_state.clone(); - let divider = window.with_id(col_idx, |window| { - let mut resize_divider = div() - .id(col_idx) - .absolute() - .top_0() - .left(divider_left) - .w(px(RESIZE_DIVIDER_WIDTH)) - .h_full() - .bg(cx.theme().colors().border.opacity(0.8)); - - let mut resize_handle = div() - .id("column-resize-handle") - .absolute() - .left_neg_0p5() - .w(px(RESIZE_COLUMN_WIDTH)) - .h_full(); - - if resize_behavior[col_idx].is_resizable() { - let is_highlighted = window.use_state(cx, |_window, _cx| false); - - resize_divider = resize_divider.when(*is_highlighted.read(cx), |div| { - div.bg(cx.theme().colors().border_focused) + let divider = div().id(col_idx).absolute().top_0().left(divider_left); + let entity_id = columns_state.entity_id(); + let on_reset: Rc = { + let columns_state = columns_state.clone(); + Rc::new(move |_window, cx| { + columns_state.update(cx, |state, cx| { + state.reset_column_to_initial_width(col_idx); + cx.notify(); }); - - resize_handle = resize_handle - .on_hover({ - let is_highlighted = is_highlighted.clone(); - move |&was_hovered, _, cx| is_highlighted.write(cx, was_hovered) - }) - .cursor_col_resize() - .on_click({ - let columns_state = columns_state.clone(); - move |event: &ClickEvent, _window, cx| { - if event.click_count() >= 2 { - columns_state.update(cx, |state, cx| { - state.reset_column_to_initial_width(col_idx); - cx.notify(); - }); - } - cx.stop_propagation(); - } - }) - .on_drag( - DraggedColumn { - col_idx, - state_id: columns_state.entity_id(), - }, - { - let is_highlighted = is_highlighted.clone(); - move |_, _offset, _window, cx| { - is_highlighted.write(cx, true); - cx.new(|_cx| Empty) - } - }, - ) - .on_drop::(move |_, _, cx| { - is_highlighted.write(cx, false); - }); - } - - resize_divider.child(resize_handle).into_any_element() - }); - dividers.push(divider); + }) + }; + dividers.push(render_column_resize_divider( + divider, + col_idx, + resize_behavior[col_idx].is_resizable(), + entity_id, + on_reset, + None, + window, + cx, + )); } } diff --git a/crates/ui/src/components/redistributable_columns.rs b/crates/ui/src/components/redistributable_columns.rs index e73711feca2740a03b0e31e9edf439d229a52b97..941017774a7d9efa48b0b2f0401f62a68781fd05 100644 --- a/crates/ui/src/components/redistributable_columns.rs +++ b/crates/ui/src/components/redistributable_columns.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use gpui::{ AbsoluteLength, AppContext as _, Bounds, DefiniteLength, DragMoveEvent, Empty, Entity, - EntityId, Length, WeakEntity, + EntityId, Length, Stateful, WeakEntity, }; use itertools::intersperse_with; @@ -442,74 +442,35 @@ pub fn render_redistributable_columns_resize_handles( let columns_state = columns_state.clone(); column_ix += 1; - window.with_id(current_column_ix, |window| { - let mut resize_divider = div() - .id(current_column_ix) - .relative() - .top_0() - .w(px(RESIZE_DIVIDER_WIDTH)) - .h_full() - .bg(cx.theme().colors().border.opacity(0.8)); - - let mut resize_handle = div() - .id("column-resize-handle") - .absolute() - .left_neg_0p5() - .w(px(RESIZE_COLUMN_WIDTH)) - .h_full(); - - if resize_behavior[current_column_ix].is_resizable() { - let is_highlighted = window.use_state(cx, |_window, _cx| false); - - resize_divider = resize_divider.when(*is_highlighted.read(cx), |div| { - div.bg(cx.theme().colors().border_focused) - }); - - resize_handle = resize_handle - .on_hover({ - let is_highlighted = is_highlighted.clone(); - move |&was_hovered, _, cx| is_highlighted.write(cx, was_hovered) - }) - .cursor_col_resize() - .on_click({ - let columns_state = columns_state.clone(); - move |event, window, cx| { - if event.click_count() >= 2 { - columns_state.update(cx, |columns, cx| { - columns.reset_column_to_initial_width( - current_column_ix, - window, - ); - cx.notify(); - }); - } - - cx.stop_propagation(); - } - }) - .on_drag( - DraggedColumn { - col_idx: current_column_ix, - state_id: columns_state.entity_id(), - }, - { - let is_highlighted = is_highlighted.clone(); - move |_, _offset, _window, cx| { - is_highlighted.write(cx, true); - cx.new(|_cx| Empty) - } - }, - ) - .on_drop::(move |_, _, cx| { - is_highlighted.write(cx, false); - columns_state.update(cx, |state, _| { - state.commit_preview(); - }); + { + let divider = div().id(current_column_ix).relative().top_0(); + let entity_id = columns_state.entity_id(); + let on_reset: Rc = { + let columns_state = columns_state.clone(); + Rc::new(move |window, cx| { + columns_state.update(cx, |columns, cx| { + columns.reset_column_to_initial_width(current_column_ix, window); + cx.notify(); }); - } - - resize_divider.child(resize_handle).into_any_element() - }) + }) + }; + let on_drag_end: Option> = { + let columns_state = columns_state.clone(); + Some(Rc::new(move |cx| { + columns_state.update(cx, |state, _| state.commit_preview()); + })) + }; + render_column_resize_divider( + divider, + current_column_ix, + resize_behavior[current_column_ix].is_resizable(), + entity_id, + on_reset, + on_drag_end, + window, + cx, + ) + } }, ); @@ -522,6 +483,83 @@ pub fn render_redistributable_columns_resize_handles( .into_any_element() } +/// Builds a single column resize divider with an interactive drag handle. +/// +/// The caller provides: +/// - `divider`: a pre-positioned divider element (with absolute or relative positioning) +/// - `col_idx`: which column this divider is for +/// - `is_resizable`: whether the column supports resizing +/// - `entity_id`: the `EntityId` of the owning column state (for the drag payload) +/// - `on_reset`: called on double-click to reset the column to its initial width +/// - `on_drag_end`: called when the drag ends (e.g. to commit preview widths) +pub(crate) fn render_column_resize_divider( + divider: Stateful
, + col_idx: usize, + is_resizable: bool, + entity_id: EntityId, + on_reset: Rc, + on_drag_end: Option>, + window: &mut Window, + cx: &mut App, +) -> AnyElement { + window.with_id(col_idx, |window| { + let mut resize_divider = divider.w(px(RESIZE_DIVIDER_WIDTH)).h_full().bg(cx + .theme() + .colors() + .border + .opacity(0.8)); + + let mut resize_handle = div() + .id("column-resize-handle") + .absolute() + .left_neg_0p5() + .w(px(RESIZE_COLUMN_WIDTH)) + .h_full(); + + if is_resizable { + let is_highlighted = window.use_state(cx, |_window, _cx| false); + + resize_divider = resize_divider.when(*is_highlighted.read(cx), |div| { + div.bg(cx.theme().colors().border_focused) + }); + + resize_handle = resize_handle + .on_hover({ + let is_highlighted = is_highlighted.clone(); + move |&was_hovered, _, cx| is_highlighted.write(cx, was_hovered) + }) + .cursor_col_resize() + .on_click(move |event, window, cx| { + if event.click_count() >= 2 { + on_reset(window, cx); + } + cx.stop_propagation(); + }) + .on_drag( + DraggedColumn { + col_idx, + state_id: entity_id, + }, + { + let is_highlighted = is_highlighted.clone(); + move |_, _offset, _window, cx| { + is_highlighted.write(cx, true); + cx.new(|_cx| Empty) + } + }, + ) + .on_drop::(move |_, _, cx| { + is_highlighted.write(cx, false); + if let Some(on_drag_end) = &on_drag_end { + on_drag_end(cx); + } + }); + } + + resize_divider.child(resize_handle).into_any_element() + }) +} + fn resize_spacer(width: Length) -> Div { div().w(width).h_full() }