Skip to repository content749 lines · 25.5 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T03:35:24.207Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
redistributable_columns.rs
1use super::data_table::{
2 ResizableColumnsState,
3 table_row::{IntoTableRow as _, TableRow},
4};
5use crate::{
6 ActiveTheme as _, AnyElement, App, Context, Div, FluentBuilder as _, InteractiveElement,
7 IntoElement, ParentElement, Pixels, StatefulInteractiveElement, Styled, Window, div, h_flex,
8 px,
9};
10use gpui::{
11 AbsoluteLength, AppContext as _, Bounds, DefiniteLength, DragMoveEvent, Empty, Entity,
12 EntityId, Length, Stateful, WeakEntity,
13};
14use std::rc::Rc;
15
16pub(crate) const RESIZE_COLUMN_WIDTH: f32 = 8.0;
17pub(crate) const RESIZE_DIVIDER_WIDTH: f32 = 1.0;
18
19/// Drag payload for column resize handles.
20/// Includes the `EntityId` of the owning column state so that
21/// `on_drag_move` handlers on unrelated tables ignore the event.
22#[derive(Debug)]
23pub(crate) struct DraggedColumn {
24 pub(crate) col_idx: usize,
25 pub(crate) state_id: EntityId,
26}
27
28#[derive(Debug, Copy, Clone, PartialEq)]
29pub enum TableResizeBehavior {
30 None,
31 Resizable,
32 MinSize(f32),
33}
34
35impl TableResizeBehavior {
36 pub fn is_resizable(&self) -> bool {
37 *self != TableResizeBehavior::None
38 }
39
40 pub fn min_size(&self) -> Option<f32> {
41 match self {
42 TableResizeBehavior::None => None,
43 TableResizeBehavior::Resizable => Some(0.05),
44 TableResizeBehavior::MinSize(min_size) => Some(*min_size),
45 }
46 }
47}
48
49#[derive(Clone)]
50pub(crate) enum ColumnsStateRef {
51 Redistributable(WeakEntity<RedistributableColumnsState>),
52 Resizable(WeakEntity<ResizableColumnsState>),
53}
54
55#[derive(Clone)]
56pub struct HeaderResizeInfo {
57 pub(crate) columns_state: ColumnsStateRef,
58 pub resize_behavior: TableRow<TableResizeBehavior>,
59}
60
61impl HeaderResizeInfo {
62 pub fn from_redistributable(
63 columns_state: &Entity<RedistributableColumnsState>,
64 cx: &App,
65 ) -> Self {
66 let resize_behavior = columns_state.read(cx).resize_behavior().clone();
67 Self {
68 columns_state: ColumnsStateRef::Redistributable(columns_state.downgrade()),
69 resize_behavior,
70 }
71 }
72
73 pub fn from_resizable(columns_state: &Entity<ResizableColumnsState>, cx: &App) -> Self {
74 let resize_behavior = columns_state.read(cx).resize_behavior().clone();
75 Self {
76 columns_state: ColumnsStateRef::Resizable(columns_state.downgrade()),
77 resize_behavior,
78 }
79 }
80
81 pub fn reset_column(&self, col_idx: usize, window: &mut Window, cx: &mut App) {
82 match &self.columns_state {
83 ColumnsStateRef::Redistributable(weak) => {
84 weak.update(cx, |state, cx| {
85 state.reset_column_to_initial_width(col_idx, window);
86 cx.notify();
87 })
88 .ok();
89 }
90 ColumnsStateRef::Resizable(weak) => {
91 weak.update(cx, |state, cx| {
92 state.reset_column_to_initial_width(col_idx);
93 cx.notify();
94 })
95 .ok();
96 }
97 }
98 }
99}
100
101pub struct RedistributableColumnsState {
102 pub(crate) initial_widths: TableRow<DefiniteLength>,
103 pub(crate) committed_widths: TableRow<DefiniteLength>,
104 pub(crate) preview_widths: TableRow<DefiniteLength>,
105 pub(crate) resize_behavior: TableRow<TableResizeBehavior>,
106 pub(crate) cached_container_width: Pixels,
107}
108
109impl RedistributableColumnsState {
110 pub fn new(
111 cols: usize,
112 initial_widths: Vec<impl Into<DefiniteLength>>,
113 resize_behavior: Vec<TableResizeBehavior>,
114 ) -> Self {
115 let widths: TableRow<DefiniteLength> = initial_widths
116 .into_iter()
117 .map(Into::into)
118 .collect::<Vec<_>>()
119 .into_table_row(cols);
120 Self {
121 initial_widths: widths.clone(),
122 committed_widths: widths.clone(),
123 preview_widths: widths,
124 resize_behavior: resize_behavior.into_table_row(cols),
125 cached_container_width: Default::default(),
126 }
127 }
128
129 pub fn cols(&self) -> usize {
130 self.committed_widths.cols()
131 }
132
133 pub fn initial_widths(&self) -> &TableRow<DefiniteLength> {
134 &self.initial_widths
135 }
136
137 pub fn preview_widths(&self) -> &TableRow<DefiniteLength> {
138 &self.preview_widths
139 }
140
141 pub fn resize_behavior(&self) -> &TableRow<TableResizeBehavior> {
142 &self.resize_behavior
143 }
144
145 pub fn widths_to_render(&self) -> TableRow<Length> {
146 self.preview_widths.map_cloned(Length::Definite)
147 }
148
149 pub fn preview_fractions(&self, rem_size: Pixels) -> TableRow<f32> {
150 if self.cached_container_width > px(0.) {
151 self.preview_widths
152 .map_ref(|length| Self::get_fraction(length, self.cached_container_width, rem_size))
153 } else {
154 self.preview_widths.map_ref(|length| match length {
155 DefiniteLength::Fraction(fraction) => *fraction,
156 DefiniteLength::Absolute(_) => 0.0,
157 })
158 }
159 }
160
161 pub fn preview_column_width(&self, column_index: usize, window: &Window) -> Option<Pixels> {
162 let width = self.preview_widths().as_slice().get(column_index)?;
163 match width {
164 DefiniteLength::Fraction(fraction) if self.cached_container_width > px(0.) => {
165 Some(self.cached_container_width * *fraction)
166 }
167 DefiniteLength::Fraction(_) => None,
168 DefiniteLength::Absolute(AbsoluteLength::Pixels(pixels)) => Some(*pixels),
169 DefiniteLength::Absolute(AbsoluteLength::Rems(rems_width)) => {
170 Some(rems_width.to_pixels(window.rem_size()))
171 }
172 }
173 }
174
175 pub fn cached_container_width(&self) -> Pixels {
176 self.cached_container_width
177 }
178
179 pub fn set_cached_container_width(&mut self, width: Pixels) {
180 self.cached_container_width = width;
181 }
182
183 pub fn commit_preview(&mut self) {
184 self.committed_widths = self.preview_widths.clone();
185 }
186
187 pub fn reset_column_to_initial_width(&mut self, column_index: usize, window: &Window) {
188 let bounds_width = self.cached_container_width;
189 if bounds_width <= px(0.) {
190 return;
191 }
192
193 let rem_size = window.rem_size();
194 let initial_sizes = self
195 .initial_widths
196 .map_ref(|length| Self::get_fraction(length, bounds_width, rem_size));
197 let widths = self
198 .committed_widths
199 .map_ref(|length| Self::get_fraction(length, bounds_width, rem_size));
200
201 let updated_widths =
202 Self::reset_to_initial_size(column_index, widths, initial_sizes, &self.resize_behavior);
203 self.committed_widths = updated_widths.map(DefiniteLength::Fraction);
204 self.preview_widths = self.committed_widths.clone();
205 }
206
207 fn get_fraction(length: &DefiniteLength, bounds_width: Pixels, rem_size: Pixels) -> f32 {
208 match length {
209 DefiniteLength::Absolute(AbsoluteLength::Pixels(pixels)) => *pixels / bounds_width,
210 DefiniteLength::Absolute(AbsoluteLength::Rems(rems_width)) => {
211 rems_width.to_pixels(rem_size) / bounds_width
212 }
213 DefiniteLength::Fraction(fraction) => *fraction,
214 }
215 }
216
217 pub(crate) fn reset_to_initial_size(
218 col_idx: usize,
219 mut widths: TableRow<f32>,
220 initial_sizes: TableRow<f32>,
221 resize_behavior: &TableRow<TableResizeBehavior>,
222 ) -> TableRow<f32> {
223 let diff = initial_sizes[col_idx] - widths[col_idx];
224
225 let left_diff =
226 initial_sizes[..col_idx].iter().sum::<f32>() - widths[..col_idx].iter().sum::<f32>();
227 let right_diff = initial_sizes[col_idx + 1..].iter().sum::<f32>()
228 - widths[col_idx + 1..].iter().sum::<f32>();
229
230 let go_left_first = if diff < 0.0 {
231 left_diff > right_diff
232 } else {
233 left_diff < right_diff
234 };
235
236 if !go_left_first {
237 let diff_remaining =
238 Self::propagate_resize_diff(diff, col_idx, &mut widths, resize_behavior, 1);
239
240 if diff_remaining != 0.0 && col_idx > 0 {
241 Self::propagate_resize_diff(
242 diff_remaining,
243 col_idx,
244 &mut widths,
245 resize_behavior,
246 -1,
247 );
248 }
249 } else {
250 let diff_remaining =
251 Self::propagate_resize_diff(diff, col_idx, &mut widths, resize_behavior, -1);
252
253 if diff_remaining != 0.0 {
254 Self::propagate_resize_diff(
255 diff_remaining,
256 col_idx,
257 &mut widths,
258 resize_behavior,
259 1,
260 );
261 }
262 }
263
264 widths
265 }
266
267 fn on_drag_move(
268 &mut self,
269 drag_event: &DragMoveEvent<DraggedColumn>,
270 hidden: Option<&TableRow<bool>>,
271 window: &mut Window,
272 cx: &mut Context<Self>,
273 ) {
274 let drag_position = drag_event.event.position;
275 let bounds = drag_event.bounds;
276 let bounds_width = bounds.right() - bounds.left();
277 if bounds_width <= px(0.) {
278 return;
279 }
280
281 let rem_size = window.rem_size();
282 let col_idx = drag_event.drag(cx).col_idx;
283
284 let divider_width = Self::get_fraction(
285 &DefiniteLength::Absolute(AbsoluteLength::Pixels(px(RESIZE_DIVIDER_WIDTH))),
286 bounds_width,
287 rem_size,
288 );
289
290 let widths = self
291 .committed_widths
292 .map_ref(|length| Self::get_fraction(length, bounds_width, rem_size));
293
294 let drag_fraction = (drag_position.x - bounds.left()) / bounds_width;
295
296 let widths = Self::compute_drag_preview(
297 widths,
298 &self.resize_behavior,
299 hidden,
300 col_idx,
301 drag_fraction,
302 divider_width,
303 );
304
305 self.preview_widths = widths.map(DefiniteLength::Fraction);
306 }
307
308 /// Computes the preview column fractions produced by dragging the divider after `col_idx`
309 /// to `drag_fraction` (the cursor's x position expressed as a fraction of the container
310 /// width). `divider_width` is the resize-divider width as a fraction of the container.
311 ///
312 /// The on-screen layout only contains the visible columns, with the hidden columns' width
313 /// budget redistributed across them (see [`redistribute_hidden_widths`]), so the geometry
314 /// here is done in that visible/redistributed space: the raw `widths` are compacted to the
315 /// visible columns and scaled to match the rendered layout, the drag is applied there (which
316 /// also makes neighbor propagation skip hidden columns), and the result is mapped back to
317 /// the raw widths, leaving hidden columns untouched.
318 ///
319 /// Extracted as a pure function so the drag math can be unit tested, mirroring the
320 /// `drag_column_handle` / `propagate_resize_diff` helpers.
321 pub(crate) fn compute_drag_preview(
322 mut widths: TableRow<f32>,
323 resize_behavior: &TableRow<TableResizeBehavior>,
324 hidden: Option<&TableRow<bool>>,
325 col_idx: usize,
326 drag_fraction: f32,
327 divider_width: f32,
328 ) -> TableRow<f32> {
329 let visible_cols: Vec<usize> = (0..widths.cols())
330 .filter(|idx| !is_column_hidden(hidden, *idx))
331 .collect();
332
333 // Dividers are only rendered after visible columns, so a hidden `col_idx` should be
334 // impossible; bail out rather than resizing the wrong column.
335 let Some(divider_position) = visible_cols.iter().position(|&idx| idx == col_idx) else {
336 return widths;
337 };
338
339 let total_sum: f32 = widths.as_slice().iter().sum();
340 let visible_sum: f32 = visible_cols.iter().map(|&idx| widths[idx]).sum();
341 // The drag only moves width between visible columns, so `visible_sum` (and therefore
342 // this scale) is the same before and after the drag, making the mapping back exact.
343 let scale = if visible_sum > 0.0 {
344 total_sum / visible_sum
345 } else {
346 1.0
347 };
348
349 let mut rendered_widths = TableRow::from_vec(
350 visible_cols
351 .iter()
352 .map(|&idx| widths[idx] * scale)
353 .collect(),
354 visible_cols.len(),
355 );
356 let rendered_behavior = TableRow::from_vec(
357 visible_cols
358 .iter()
359 .map(|&idx| resize_behavior[idx])
360 .collect(),
361 visible_cols.len(),
362 );
363
364 let mut col_position = 0.0;
365 for length in rendered_widths[0..=divider_position].iter() {
366 col_position += length + divider_width;
367 }
368
369 let mut total_length_ratio = col_position;
370 for length in rendered_widths[divider_position + 1..].iter() {
371 total_length_ratio += length;
372 }
373 let cols = rendered_behavior.cols();
374 total_length_ratio += (cols - 1 - divider_position) as f32 * divider_width;
375
376 let drag_fraction = drag_fraction * total_length_ratio;
377 let diff = drag_fraction - col_position - divider_width / 2.0;
378
379 Self::drag_column_handle(
380 diff,
381 divider_position,
382 &mut rendered_widths,
383 &rendered_behavior,
384 );
385
386 for (visible_position, &idx) in visible_cols.iter().enumerate() {
387 widths[idx] = rendered_widths[visible_position] / scale;
388 }
389
390 widths
391 }
392
393 pub(crate) fn drag_column_handle(
394 diff: f32,
395 col_idx: usize,
396 widths: &mut TableRow<f32>,
397 resize_behavior: &TableRow<TableResizeBehavior>,
398 ) {
399 if diff > 0.0 {
400 Self::propagate_resize_diff(diff, col_idx, widths, resize_behavior, 1);
401 } else {
402 Self::propagate_resize_diff(-diff, col_idx + 1, widths, resize_behavior, -1);
403 }
404 }
405
406 pub(crate) fn propagate_resize_diff(
407 diff: f32,
408 col_idx: usize,
409 widths: &mut TableRow<f32>,
410 resize_behavior: &TableRow<TableResizeBehavior>,
411 direction: i8,
412 ) -> f32 {
413 let mut diff_remaining = diff;
414 if resize_behavior[col_idx].min_size().is_none() {
415 return diff;
416 }
417
418 let step_right;
419 let step_left;
420 if direction < 0 {
421 step_right = 0;
422 step_left = 1;
423 } else {
424 step_right = 1;
425 step_left = 0;
426 }
427 if col_idx == 0 && direction < 0 {
428 return diff;
429 }
430 let mut curr_column = col_idx + step_right - step_left;
431
432 while diff_remaining != 0.0 && curr_column < widths.cols() {
433 let Some(min_size) = resize_behavior[curr_column].min_size() else {
434 if curr_column == 0 {
435 break;
436 }
437 curr_column -= step_left;
438 curr_column += step_right;
439 continue;
440 };
441
442 let curr_width = widths[curr_column] - diff_remaining;
443 widths[curr_column] = curr_width;
444
445 if min_size > curr_width {
446 diff_remaining = min_size - curr_width;
447 widths[curr_column] = min_size;
448 } else {
449 diff_remaining = 0.0;
450 break;
451 }
452 if curr_column == 0 {
453 break;
454 }
455 curr_column -= step_left;
456 curr_column += step_right;
457 }
458 widths[col_idx] = widths[col_idx] + (diff - diff_remaining);
459
460 diff_remaining
461 }
462}
463
464/// Returns `true` when the column at `idx` is hidden by `hidden`.
465pub fn is_column_hidden(hidden: Option<&TableRow<bool>>, idx: usize) -> bool {
466 hidden
467 .and_then(|mask| mask.get(idx).copied())
468 .unwrap_or(false)
469}
470
471/// Redistributes the fractional width budget of hidden columns across the visible columns so the
472/// visible columns fill the container instead of leaving a gap. Hidden columns keep their stored
473/// width (they are never rendered, so the value is not shown) and `Absolute` widths are left
474/// untouched. Returns the widths unchanged when no column is hidden.
475pub fn redistribute_hidden_widths(
476 widths: &TableRow<Length>,
477 hidden: Option<&TableRow<bool>>,
478) -> TableRow<Length> {
479 if !(0..widths.cols()).any(|idx| is_column_hidden(hidden, idx)) {
480 return widths.clone();
481 }
482
483 let mut total_fraction_sum = 0.0;
484 let mut visible_fraction_sum = 0.0;
485 for (idx, width) in widths.as_slice().iter().enumerate() {
486 if let Length::Definite(DefiniteLength::Fraction(fraction)) = width {
487 total_fraction_sum += *fraction;
488 if !is_column_hidden(hidden, idx) {
489 visible_fraction_sum += *fraction;
490 }
491 }
492 }
493 let scale = if visible_fraction_sum > 0.0 {
494 total_fraction_sum / visible_fraction_sum
495 } else {
496 1.0
497 };
498
499 let scaled: Vec<Length> = widths
500 .as_slice()
501 .iter()
502 .enumerate()
503 .map(|(idx, width)| match width {
504 Length::Definite(DefiniteLength::Fraction(fraction))
505 if !is_column_hidden(hidden, idx) =>
506 {
507 Length::Definite(DefiniteLength::Fraction(fraction * scale))
508 }
509 other => *other,
510 })
511 .collect();
512 TableRow::from_vec(scaled, widths.cols())
513}
514
515/// Fraction-valued counterpart of [`redistribute_hidden_widths`].
516pub fn redistribute_hidden_fractions(
517 fractions: &TableRow<f32>,
518 hidden: Option<&TableRow<bool>>,
519) -> TableRow<f32> {
520 if !(0..fractions.cols()).any(|idx| is_column_hidden(hidden, idx)) {
521 return fractions.clone();
522 }
523
524 let total_sum: f32 = fractions.as_slice().iter().sum();
525 let visible_sum: f32 = fractions
526 .as_slice()
527 .iter()
528 .enumerate()
529 .filter(|(idx, _)| !is_column_hidden(hidden, *idx))
530 .map(|(_, fraction)| *fraction)
531 .sum();
532 let scale = if visible_sum > 0.0 {
533 total_sum / visible_sum
534 } else {
535 1.0
536 };
537
538 let scaled: Vec<f32> = fractions
539 .as_slice()
540 .iter()
541 .enumerate()
542 .map(|(idx, fraction)| {
543 if is_column_hidden(hidden, idx) {
544 *fraction
545 } else {
546 fraction * scale
547 }
548 })
549 .collect();
550 TableRow::from_vec(scaled, fractions.cols())
551}
552
553pub fn bind_redistributable_columns(
554 container: Div,
555 columns_state: Entity<RedistributableColumnsState>,
556 hidden: Option<TableRow<bool>>,
557) -> Div {
558 container
559 .on_drag_move::<DraggedColumn>({
560 let columns_state = columns_state.clone();
561 move |event, window, cx| {
562 if event.drag(cx).state_id != columns_state.entity_id() {
563 return;
564 }
565 columns_state.update(cx, |columns, cx| {
566 columns.on_drag_move(event, hidden.as_ref(), window, cx);
567 });
568 }
569 })
570 .on_children_prepainted({
571 let columns_state = columns_state.clone();
572 move |bounds, _, cx| {
573 if let Some(width) = child_bounds_width(&bounds) {
574 columns_state.update(cx, |columns, _| {
575 columns.set_cached_container_width(width);
576 });
577 }
578 }
579 })
580 .on_drop::<DraggedColumn>(move |_, _, cx| {
581 columns_state.update(cx, |columns, _| {
582 columns.commit_preview();
583 });
584 })
585}
586
587pub fn render_redistributable_columns_resize_handles(
588 columns_state: &Entity<RedistributableColumnsState>,
589 hidden: Option<&TableRow<bool>>,
590 window: &mut Window,
591 cx: &mut App,
592) -> AnyElement {
593 let (column_widths, resize_behavior) = {
594 let state = columns_state.read(cx);
595 (
596 redistribute_hidden_widths(&state.widths_to_render(), hidden),
597 state.resize_behavior().clone(),
598 )
599 };
600
601 // Only the visible columns participate in the layout; filtered columns are skipped entirely
602 // (no spacer, no divider) so we don't draw a stray resize line where a hidden column was.
603 let visible_cols: Vec<usize> = (0..column_widths.cols())
604 .filter(|idx| !is_column_hidden(hidden, *idx))
605 .collect();
606
607 let mut children: Vec<AnyElement> = Vec::with_capacity(visible_cols.len() * 2);
608 for (position, &col_idx) in visible_cols.iter().enumerate() {
609 children.push(resize_spacer(column_widths[col_idx]).into_any_element());
610
611 // A divider is rendered after every visible column except the last, mirroring the
612 // original `intersperse` behavior but in terms of visible columns.
613 let is_last_visible = position + 1 == visible_cols.len();
614 if is_last_visible {
615 continue;
616 }
617
618 let columns_state = columns_state.clone();
619 let divider = div().id(col_idx).relative().top_0();
620 let entity_id = columns_state.entity_id();
621 let on_reset: Rc<dyn Fn(&mut Window, &mut App)> = {
622 let columns_state = columns_state.clone();
623 Rc::new(move |window, cx| {
624 columns_state.update(cx, |columns, cx| {
625 columns.reset_column_to_initial_width(col_idx, window);
626 cx.notify();
627 });
628 })
629 };
630 let on_drag_end: Option<Rc<dyn Fn(&mut App)>> = {
631 Some(Rc::new(move |cx| {
632 columns_state.update(cx, |state, _| state.commit_preview());
633 }))
634 };
635 children.push(render_column_resize_divider(
636 divider,
637 col_idx,
638 resize_behavior[col_idx].is_resizable(),
639 entity_id,
640 on_reset,
641 on_drag_end,
642 window,
643 cx,
644 ));
645 }
646
647 h_flex()
648 .id("resize-handles")
649 .absolute()
650 .inset_0()
651 .w_full()
652 .children(children)
653 .into_any_element()
654}
655
656/// Builds a single column resize divider with an interactive drag handle.
657///
658/// The caller provides:
659/// - `divider`: a pre-positioned divider element (with absolute or relative positioning)
660/// - `col_idx`: which column this divider is for
661/// - `is_resizable`: whether the column supports resizing
662/// - `entity_id`: the `EntityId` of the owning column state (for the drag payload)
663/// - `on_reset`: called on double-click to reset the column to its initial width
664/// - `on_drag_end`: called when the drag ends (e.g. to commit preview widths)
665pub(crate) fn render_column_resize_divider(
666 divider: Stateful<Div>,
667 col_idx: usize,
668 is_resizable: bool,
669 entity_id: EntityId,
670 on_reset: Rc<dyn Fn(&mut Window, &mut App)>,
671 on_drag_end: Option<Rc<dyn Fn(&mut App)>>,
672 window: &mut Window,
673 cx: &mut App,
674) -> AnyElement {
675 window.with_id(col_idx, |window| {
676 let mut resize_divider = divider.w(px(RESIZE_DIVIDER_WIDTH)).h_full().bg(cx
677 .theme()
678 .colors()
679 .border
680 .opacity(0.8));
681
682 let mut resize_handle = div()
683 .id("column-resize-handle")
684 .absolute()
685 .left_neg_0p5()
686 .w(px(RESIZE_COLUMN_WIDTH))
687 .h_full();
688
689 if is_resizable {
690 let is_highlighted = window.use_state(cx, |_window, _cx| false);
691
692 resize_divider = resize_divider.when(*is_highlighted.read(cx), |div| {
693 div.bg(cx.theme().colors().border_focused)
694 });
695
696 resize_handle = resize_handle
697 .on_hover({
698 let is_highlighted = is_highlighted.clone();
699 move |&was_hovered, _, cx| is_highlighted.write(cx, was_hovered)
700 })
701 .cursor_col_resize()
702 .on_click(move |event, window, cx| {
703 if event.click_count() >= 2 {
704 on_reset(window, cx);
705 }
706 cx.stop_propagation();
707 })
708 .on_drag(
709 DraggedColumn {
710 col_idx,
711 state_id: entity_id,
712 },
713 {
714 let is_highlighted = is_highlighted.clone();
715 move |_, _offset, _window, cx| {
716 is_highlighted.write(cx, true);
717 cx.new(|_cx| Empty)
718 }
719 },
720 )
721 .on_drop::<DraggedColumn>(move |_, _, cx| {
722 is_highlighted.write(cx, false);
723 if let Some(on_drag_end) = &on_drag_end {
724 on_drag_end(cx);
725 }
726 });
727 }
728
729 resize_divider.child(resize_handle).into_any_element()
730 })
731}
732
733fn resize_spacer(width: Length) -> Div {
734 div().w(width).h_full()
735}
736
737fn child_bounds_width(bounds: &[Bounds<Pixels>]) -> Option<Pixels> {
738 let first_bounds = bounds.first()?;
739 let mut left = first_bounds.left();
740 let mut right = first_bounds.right();
741
742 for bound in bounds.iter().skip(1) {
743 left = left.min(bound.left());
744 right = right.max(bound.right());
745 }
746
747 Some(right - left)
748}
749