Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:43:46.855Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

redistributable_columns.rs

749 lines · 25.5 KB · rust
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
Served at tenant.openagents/omega Member data and write actions are omitted.