1use crate::presenters::ContentPresenter;
5use crate::primitives::{AdornerLayer, TemplatedControlImpl, Thumb, ThumbImpl, ThumbImplExt};
6use crate::templates::ITemplateOf;
7use crate::{
8 ColumnDefinition, Control, ControlImpl, Decorator, DefinitionBase, Grid, GridLength, GridUnitType, RowDefinition,
9};
10use ferroui_base::input::navigation::XYFocusHelpers;
11use ferroui_base::input::{
12 Cursor, FocusChangedEventArgs, InputElementImpl, InputElementImplExt, Key, KeyEventArgs, PointerEventArgs,
13 StandardCursorType, VectorEventArgs,
14};
15use ferroui_base::interactivity::InteractiveImpl;
16use ferroui_base::layout::{HorizontalAlignment, LayoutHelper, LayoutableImpl, LayoutableImplExt, VerticalAlignment};
17use ferroui_base::media::{Geometry, ITransform, TranslateTransform};
18use ferroui_base::utilities::MathUtilities;
19use ferroui_base::{
20 ferro_class, ferro_impl_classes, ferro_property, instantiate, FerroObjectImpl, FerroProperty, Ref, Size,
21 StyledElement, StyledElementImpl, StyledProperty, VisualImpl,
22};
23use std::cell::{Cell, RefCell};
24use std::rc::Rc;
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
28#[repr(i32)]
29pub enum GridResizeDirection {
30 Auto = 0,
33 Columns = 1,
35 Rows = 2,
37}
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
41#[repr(i32)]
42pub enum GridResizeBehavior {
43 BasedOnAlignment = 0,
45 CurrentAndNext = 1,
47 PreviousAndCurrent = 2,
49 PreviousAndNext = 3,
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq)]
57enum SplitBehavior {
58 Split,
60 Resize1,
62 Resize2,
64}
65
66#[derive(Clone)]
68struct ResizeData {
69 shows_preview: bool,
70 adorner: Option<Ref<PreviewAdorner>>,
71
72 min_change: f64,
74 max_change: f64,
75
76 grid: Ref<Grid>,
78
79 resize_direction: GridResizeDirection,
81 resize_behavior: GridResizeBehavior,
82
83 definition1: Option<Ref<DefinitionBase>>,
85 definition2: Option<Ref<DefinitionBase>>,
86
87 split_behavior: SplitBehavior,
89
90 splitter_index: i32,
92
93 definition1_index: i32,
95 definition2_index: i32,
96
97 original_definition1_length: GridLength,
100 original_definition2_length: GridLength,
101 original_definition1_actual_length: f64,
102 original_definition2_actual_length: f64,
103
104 splitter_length: f64,
108
109 scaling: f64,
111}
112
113ferroui_base::ferro_thread_local! {
114 static COLUMN_SPLITTER_CURSOR: RefCell<Option<Rc<Cursor>>> = const { RefCell::new(None) };
115 static ROW_SPLITTER_CURSOR: RefCell<Option<Rc<Cursor>>> = const { RefCell::new(None) };
116}
117
118#[repr(C)]
121pub struct GridSplitter {
122 base: Thumb,
123 resize_data: RefCell<Option<ResizeData>>,
124 is_focus_engaged: Cell<bool>,
125}
126
127ferro_class! {
128 GridSplitter: Thumb, virtuals GridSplitterImpl: ThumbImpl {
129 fn get_parent_grid(this) -> Option<Ref<Grid>>;
140 fn get_properties_value_source(this) -> Ref<StyledElement>;
150 }
151}
152ferroui_base::ferro_class_info!(GridSplitter { new: GridSplitter::new });
153
154ferro_impl_classes!(
155 GridSplitter: FerroObjectImpl,
156 StyledElementImpl,
157 VisualImpl,
158 LayoutableImpl,
159 InteractiveImpl,
160 ControlImpl,
161 TemplatedControlImpl
162);
163
164impl InputElementImpl for GridSplitter {
165 fn on_pointer_entered(this: &Self, e: &PointerEventArgs) {
166 Self::parent_on_pointer_entered(this, e);
167
168 let direction = this.get_effective_resize_direction();
169
170 match direction {
171 GridResizeDirection::Columns => {
172 this.set_cursor(Some(Self::splitter_cursor(&COLUMN_SPLITTER_CURSOR, StandardCursorType::SizeWestEast)));
173 }
174 GridResizeDirection::Rows => {
175 this.set_cursor(Some(Self::splitter_cursor(&ROW_SPLITTER_CURSOR, StandardCursorType::SizeNorthSouth)));
176 }
177 GridResizeDirection::Auto => {}
178 }
179 }
180
181 fn on_lost_focus(this: &Self, e: &FocusChangedEventArgs) {
182 Self::parent_on_lost_focus(this, e);
183
184 if this.has_resize_data() {
185 this.cancel_resize();
186 }
187 }
188
189 fn on_key_down(this: &Self, e: &KeyEventArgs) {
190 let using_xy_navigation = XYFocusHelpers::is_allowed_xy_navigation_mode(this, Some(e.key_device_type));
191 let allow_arrow_keys = this.is_focus_engaged.get() || !using_xy_navigation;
192
193 match e.key {
194 Key::Enter if using_xy_navigation => {
195 this.is_focus_engaged.set(!this.is_focus_engaged.get());
196 e.set_handled(true);
197 }
198 Key::Escape => {
199 this.is_focus_engaged.set(false);
200 if this.has_resize_data() {
201 this.cancel_resize();
202 e.set_handled(true);
203 }
204 }
205 Key::Left if allow_arrow_keys => {
206 e.set_handled(this.keyboard_move_splitter(-this.keyboard_increment(), 0.0));
207 }
208 Key::Right if allow_arrow_keys => {
209 e.set_handled(this.keyboard_move_splitter(this.keyboard_increment(), 0.0));
210 }
211 Key::Up if allow_arrow_keys => {
212 e.set_handled(this.keyboard_move_splitter(0.0, -this.keyboard_increment()));
213 }
214 Key::Down if allow_arrow_keys => {
215 e.set_handled(this.keyboard_move_splitter(0.0, this.keyboard_increment()));
216 }
217 _ => {}
218 }
219 }
220}
221
222impl ThumbImpl for GridSplitter {
223 fn on_drag_started(this: &Self, e: &VectorEventArgs) {
224 Self::parent_on_drag_started(this, e);
225
226 if this.has_resize_data() {
228 return;
229 }
230
231 this.initialize_data(this.shows_preview());
232 }
233
234 fn on_drag_delta(this: &Self, e: &VectorEventArgs) {
235 Self::parent_on_drag_delta(this, e);
236
237 let Some(resize_data) = this.resize_data.borrow().clone() else {
238 return;
239 };
240
241 let mut horizontal_change = e.vector.x;
242 let mut vertical_change = e.vector.y;
243
244 let drag_increment = this.drag_increment();
246 horizontal_change = (horizontal_change / drag_increment).round_ties_even() * drag_increment;
247 vertical_change = (vertical_change / drag_increment).round_ties_even() * drag_increment;
248
249 if resize_data.shows_preview {
250 if let Some(adorner) = &resize_data.adorner {
253 if resize_data.resize_direction == GridResizeDirection::Columns {
254 adorner.set_offset_x(f64::min(
255 f64::max(horizontal_change, resize_data.min_change),
256 resize_data.max_change,
257 ));
258 } else {
259 adorner.set_offset_y(f64::min(
260 f64::max(vertical_change, resize_data.min_change),
261 resize_data.max_change,
262 ));
263 }
264 }
265 } else {
266 this.move_splitter(horizontal_change, vertical_change);
268 }
269 }
270
271 fn on_drag_completed(this: &Self, e: &VectorEventArgs) {
272 Self::parent_on_drag_completed(this, e);
273
274 let Some(resize_data) = this.resize_data.borrow().clone() else {
275 return;
276 };
277
278 if resize_data.shows_preview {
279 if let Some(adorner) = &resize_data.adorner {
282 this.move_splitter(adorner.offset_x(), adorner.offset_y());
283 this.remove_preview_adorner();
284 }
285 }
286
287 *this.resize_data.borrow_mut() = None;
288 }
289}
290
291impl GridSplitterImpl for GridSplitter {
292 fn get_parent_grid(this: &Self) -> Option<Ref<Grid>> {
293 let parent = this.parent()?;
297 if let Some(grid) = parent.clone().cast::<Grid>() {
298 return Some(grid);
299 }
300
301 let items_control = match parent.clone().cast::<crate::ItemsControl>() {
302 Some(items_control) => items_control,
303 None => {
304 parent.cast::<ContentPresenter>()?;
305 this.parent()?.parent()?.cast::<crate::ItemsControl>()?
306 }
307 };
308 items_control.items_panel_root()?.cast::<Grid>()
309 }
310
311 fn get_properties_value_source(this: &Self) -> Ref<StyledElement> {
312 match this.parent() {
313 Some(parent) if parent.is::<ContentPresenter>() => parent,
314 _ => this.to_ref().upcast(),
315 }
316 }
317}
318
319ferroui_base::ferro_properties! { impl GridSplitter {
320 ferro_property!(
321 pub fn resize_direction_property() -> StyledProperty<GridResizeDirection> {
323 FerroProperty::register::<GridSplitter, _>("ResizeDirection", GridResizeDirection::Auto)
324 }
325 );
326
327 ferro_property!(
328 pub fn resize_behavior_property() -> StyledProperty<GridResizeBehavior> {
330 FerroProperty::register::<GridSplitter, _>("ResizeBehavior", GridResizeBehavior::BasedOnAlignment)
331 }
332 );
333
334 ferro_property!(
335 pub fn shows_preview_property() -> StyledProperty<bool> {
337 FerroProperty::register::<GridSplitter, _>("ShowsPreview", false)
338 }
339 );
340
341 ferro_property!(
342 pub fn keyboard_increment_property() -> StyledProperty<f64> {
344 FerroProperty::register::<GridSplitter, _>("KeyboardIncrement", 10.0)
345 }
346 );
347
348 ferro_property!(
349 pub fn drag_increment_property() -> StyledProperty<f64> {
351 FerroProperty::register::<GridSplitter, _>("DragIncrement", 1.0)
352 }
353 );
354
355 ferro_property!(
356 pub fn preview_content_property() -> StyledProperty<Option<Rc<dyn ITemplateOf<Ref<Control>>>>> {
358 FerroProperty::register::<GridSplitter, _>("PreviewContent", None)
359 }
360 );
361} }
362
363impl GridSplitter {
364 pub fn construct() -> Self {
366 Self { base: Thumb::construct(), resize_data: RefCell::new(None), is_focus_engaged: Cell::new(false) }
367 }
368
369 pub fn new() -> Ref<Self> {
370 instantiate(Self::construct())
371 }
372
373 pub fn resize_direction(&self) -> GridResizeDirection {
375 self.get_value(Self::resize_direction_property())
376 }
377
378 pub fn set_resize_direction(&self, value: GridResizeDirection) {
379 self.set_value(Self::resize_direction_property(), value)
380 }
381
382 pub fn resize_behavior(&self) -> GridResizeBehavior {
384 self.get_value(Self::resize_behavior_property())
385 }
386
387 pub fn set_resize_behavior(&self, value: GridResizeBehavior) {
388 self.set_value(Self::resize_behavior_property(), value)
389 }
390
391 pub fn shows_preview(&self) -> bool {
394 self.get_value(Self::shows_preview_property())
395 }
396
397 pub fn set_shows_preview(&self, value: bool) {
398 self.set_value(Self::shows_preview_property(), value)
399 }
400
401 pub fn keyboard_increment(&self) -> f64 {
404 self.get_value(Self::keyboard_increment_property())
405 }
406
407 pub fn set_keyboard_increment(&self, value: f64) {
408 self.set_value(Self::keyboard_increment_property(), value)
409 }
410
411 pub fn drag_increment(&self) -> f64 {
413 self.get_value(Self::drag_increment_property())
414 }
415
416 pub fn set_drag_increment(&self, value: f64) {
417 self.set_value(Self::drag_increment_property(), value)
418 }
419
420 pub fn preview_content(&self) -> Option<Rc<dyn ITemplateOf<Ref<Control>>>> {
423 self.get_value(Self::preview_content_property())
424 }
425
426 pub fn set_preview_content(&self, value: Option<Rc<dyn ITemplateOf<Ref<Control>>>>) {
427 self.set_value(Self::preview_content_property(), value)
428 }
429
430 fn splitter_cursor(
431 slot: &'static ferroui_base::ferro_thread_local::LocalKey<RefCell<Option<Rc<Cursor>>>>,
432 cursor_type: StandardCursorType,
433 ) -> Rc<Cursor> {
434 if let Some(cursor) = slot.with(|slot| slot.borrow().clone()) {
435 return cursor;
436 }
437 let cursor = Cursor::new(cursor_type);
438 slot.with(|slot| *slot.borrow_mut() = Some(cursor.clone()));
439 cursor
440 }
441
442 fn remove_preview_adorner(&self) {
443 let adorner = self.resize_data.borrow().as_ref().and_then(|resize_data| resize_data.adorner.clone());
444 if let Some(adorner) = adorner {
445 if let Some(layer) = AdornerLayer::get_adorner_layer(self) {
446 layer.children().remove(&adorner.upcast::<Control>());
447 }
448 }
449 }
450
451 fn has_resize_data(&self) -> bool {
452 self.resize_data.borrow().is_some()
453 }
454
455 pub(crate) fn get_effective_resize_direction(&self) -> GridResizeDirection {
458 let mut direction = self.resize_direction();
459
460 if direction != GridResizeDirection::Auto {
461 return direction;
462 }
463
464 if self.horizontal_alignment() != HorizontalAlignment::Stretch {
466 direction = GridResizeDirection::Columns;
467 } else if self.vertical_alignment() != VerticalAlignment::Stretch {
468 direction = GridResizeDirection::Rows;
469 } else if self.bounds().width <= self.bounds().height {
470 direction = GridResizeDirection::Columns;
472 } else {
473 direction = GridResizeDirection::Rows;
474 }
475
476 direction
477 }
478
479 fn get_effective_resize_behavior(&self, direction: GridResizeDirection) -> GridResizeBehavior {
482 let mut resize_behavior = self.resize_behavior();
483
484 if resize_behavior == GridResizeBehavior::BasedOnAlignment {
485 if direction == GridResizeDirection::Columns {
486 resize_behavior = match self.horizontal_alignment() {
487 HorizontalAlignment::Left => GridResizeBehavior::PreviousAndCurrent,
488 HorizontalAlignment::Right => GridResizeBehavior::CurrentAndNext,
489 _ => GridResizeBehavior::PreviousAndNext,
490 };
491 } else {
492 resize_behavior = match self.vertical_alignment() {
493 VerticalAlignment::Top => GridResizeBehavior::PreviousAndCurrent,
494 VerticalAlignment::Bottom => GridResizeBehavior::CurrentAndNext,
495 _ => GridResizeBehavior::PreviousAndNext,
496 };
497 }
498 }
499
500 resize_behavior
501 }
502
503 fn initialize_data(&self, shows_preview: bool) {
505 let Some(grid) = self.get_parent_grid() else {
507 return;
508 };
509
510 let resize_direction = self.get_effective_resize_direction();
511
512 let bounds = self.bounds();
515 let resize_data = self.setup_definitions_to_resize(ResizeData {
516 grid,
517 shows_preview,
518 adorner: None,
519 min_change: 0.0,
520 max_change: 0.0,
521 resize_direction,
522 splitter_length: f64::min(bounds.width, bounds.height),
523 resize_behavior: self.get_effective_resize_behavior(resize_direction),
524 scaling: self.get_layout_root().map_or(1.0, |root| root.layout_scaling()),
525 definition1: None,
526 definition2: None,
527 split_behavior: SplitBehavior::Split,
528 splitter_index: 0,
529 definition1_index: 0,
530 definition2_index: 0,
531 original_definition1_length: GridLength::default(),
532 original_definition2_length: GridLength::default(),
533 original_definition1_actual_length: 0.0,
534 original_definition2_actual_length: 0.0,
535 });
536
537 let can_resize = resize_data.is_some();
539 *self.resize_data.borrow_mut() = resize_data;
540
541 if can_resize {
542 self.setup_preview_adorner();
544 }
545 }
546
547 fn setup_preview_adorner(&self) {
549 let Some(resize_data) = self.resize_data.borrow().clone() else {
550 return;
551 };
552
553 if resize_data.shows_preview {
554 let adorner_layer = AdornerLayer::get_adorner_layer(&resize_data.grid);
556
557 let preview_content = self.preview_content();
558
559 let Some(adorner_layer) = adorner_layer else {
561 return;
562 };
563
564 let built_preview_content = preview_content.map(|preview_content| preview_content.build_typed());
565
566 let adorner = PreviewAdorner::new(built_preview_content);
567
568 AdornerLayer::set_adorned_element(&adorner, self.to_ref().upcast::<ferroui_base::Visual>());
569 AdornerLayer::set_is_clip_enabled(&adorner, false);
570
571 adorner_layer.children().add(adorner.clone().upcast::<Control>());
572
573 let (min_change, max_change) = Self::get_delta_constraints(&resize_data);
575
576 if let Some(resize_data) = self.resize_data.borrow_mut().as_mut() {
577 resize_data.adorner = Some(adorner);
578 resize_data.min_change = min_change;
579 resize_data.max_change = max_change;
580 }
581 }
582 }
583
584 fn setup_definitions_to_resize(&self, mut resize_data: ResizeData) -> Option<ResizeData> {
587 let source_control = self.get_properties_value_source();
591 let is_columns = resize_data.resize_direction == GridResizeDirection::Columns;
592 let grid_span = source_control.get_value(if is_columns {
593 Grid::column_span_property()
594 } else {
595 Grid::row_span_property()
596 });
597
598 if grid_span == 1 {
599 let splitter_index =
600 source_control.get_value(if is_columns { Grid::column_property() } else { Grid::row_property() });
601
602 let (index1, index2) = match resize_data.resize_behavior {
604 GridResizeBehavior::PreviousAndCurrent => (splitter_index - 1, splitter_index),
606 GridResizeBehavior::CurrentAndNext => (splitter_index, splitter_index + 1),
608 _ => (splitter_index - 1, splitter_index + 1),
610 };
611
612 let count = if is_columns {
614 resize_data.grid.column_definitions().count()
615 } else {
616 resize_data.grid.row_definitions().count()
617 } as i32;
618
619 if index1 >= 0 && index2 < count {
620 resize_data.splitter_index = splitter_index;
621
622 let definition1 = Self::get_grid_definition(&resize_data.grid, index1, resize_data.resize_direction);
623 resize_data.definition1_index = index1;
624 resize_data.original_definition1_length = definition1.user_size_value_cache();
626 resize_data.original_definition1_actual_length = Self::get_actual_length(&definition1);
627
628 let definition2 = Self::get_grid_definition(&resize_data.grid, index2, resize_data.resize_direction);
629 resize_data.definition2_index = index2;
630 resize_data.original_definition2_length = definition2.user_size_value_cache();
632 resize_data.original_definition2_actual_length = Self::get_actual_length(&definition2);
633
634 let is_star1 = Self::is_star(&definition1);
636 let is_star2 = Self::is_star(&definition2);
637
638 resize_data.split_behavior = if is_star1 && is_star2 {
639 SplitBehavior::Split
641 } else if !is_star1 {
642 SplitBehavior::Resize1
645 } else {
646 SplitBehavior::Resize2
647 };
648
649 resize_data.definition1 = Some(definition1);
650 resize_data.definition2 = Some(definition2);
651
652 return Some(resize_data);
653 }
654 }
655
656 None
657 }
658
659 fn cancel_resize(&self) {
661 let Some(resize_data) = self.resize_data.borrow().clone() else {
662 return;
663 };
664
665 if resize_data.shows_preview {
667 self.remove_preview_adorner();
668 } else {
669 if let Some(definition1) = &resize_data.definition1 {
671 Self::set_definition_length(definition1, resize_data.original_definition1_length);
672 }
673 if let Some(definition2) = &resize_data.definition2 {
674 Self::set_definition_length(definition2, resize_data.original_definition2_length);
675 }
676 }
677
678 *self.resize_data.borrow_mut() = None;
679 }
680
681 fn is_star(definition: &DefinitionBase) -> bool {
683 definition.user_size_value_cache().is_star()
684 }
685
686 fn get_grid_definition(grid: &Grid, index: i32, direction: GridResizeDirection) -> Ref<DefinitionBase> {
689 if direction == GridResizeDirection::Columns {
690 grid.column_definitions().get(index as usize).upcast()
691 } else {
692 grid.row_definitions().get(index as usize).upcast()
693 }
694 }
695
696 fn get_actual_length(definition: &Ref<DefinitionBase>) -> f64 {
699 if let Some(column) = definition.cast::<ColumnDefinition>() {
700 return column.actual_width();
701 }
702
703 definition.cast::<RowDefinition>().map_or(0.0, |row| row.actual_height())
704 }
705
706 fn set_definition_length(definition: &Ref<DefinitionBase>, length: GridLength) {
708 definition.set_value(
709 if definition.is::<ColumnDefinition>() {
710 ColumnDefinition::width_property()
711 } else {
712 RowDefinition::height_property()
713 },
714 length,
715 );
716 }
717
718 fn get_delta_constraints(resize_data: &ResizeData) -> (f64, f64) {
721 let (Some(definition1), Some(definition2)) = (&resize_data.definition1, &resize_data.definition2) else {
722 return (0.0, 0.0);
723 };
724
725 let definition1_len = Self::get_actual_length(definition1);
726 let mut definition1_min = definition1.user_min_size_value_cache();
727 let definition1_max = definition1.user_max_size_value_cache();
728
729 let definition2_len = Self::get_actual_length(definition2);
730 let mut definition2_min = definition2.user_min_size_value_cache();
731 let definition2_max = definition2.user_max_size_value_cache();
732
733 if resize_data.splitter_index == resize_data.definition1_index {
735 definition1_min = f64::max(definition1_min, resize_data.splitter_length);
736 } else if resize_data.splitter_index == resize_data.definition2_index {
737 definition2_min = f64::max(definition2_min, resize_data.splitter_length);
738 }
739
740 let min_delta = -f64::min(definition1_len - definition1_min, definition2_max - definition2_len);
742 let max_delta = f64::min(definition1_max - definition1_len, definition2_len - definition2_min);
743
744 (min_delta, max_delta)
745 }
746
747 fn set_lengths(resize_data: &ResizeData, definition1_pixels: f64, definition2_pixels: f64) {
749 if resize_data.split_behavior == SplitBehavior::Split {
752 let definitions: Vec<Ref<DefinitionBase>> = if resize_data.resize_direction == GridResizeDirection::Columns
753 {
754 resize_data.grid.column_definitions().snapshot().iter().map(|d| d.clone().upcast()).collect()
755 } else {
756 resize_data.grid.row_definitions().snapshot().iter().map(|d| d.clone().upcast()).collect()
757 };
758
759 for (i, definition) in definitions.iter().enumerate() {
760 let i = i as i32;
761
762 if i == resize_data.definition1_index {
766 Self::set_definition_length(definition, GridLength::new(definition1_pixels, GridUnitType::Star));
767 } else if i == resize_data.definition2_index {
768 Self::set_definition_length(definition, GridLength::new(definition2_pixels, GridUnitType::Star));
769 } else if Self::is_star(definition) {
770 Self::set_definition_length(
771 definition,
772 GridLength::new(Self::get_actual_length(definition), GridUnitType::Star),
773 );
774 }
775 }
776 } else if resize_data.split_behavior == SplitBehavior::Resize1 {
777 if let Some(definition1) = &resize_data.definition1 {
778 Self::set_definition_length(definition1, GridLength::from_pixels(definition1_pixels));
779 }
780 } else if let Some(definition2) = &resize_data.definition2 {
781 Self::set_definition_length(definition2, GridLength::from_pixels(definition2_pixels));
782 }
783 }
784
785 fn move_splitter(&self, horizontal_change: f64, vertical_change: f64) {
788 let Some(resize_data) = self.resize_data.borrow().clone() else {
789 return;
790 };
791
792 let mut delta = if resize_data.resize_direction == GridResizeDirection::Columns {
801 horizontal_change
802 } else {
803 vertical_change
804 };
805
806 if self.use_layout_rounding() {
807 delta = LayoutHelper::round_layout_value(delta, LayoutHelper::get_layout_scale(self));
808 }
809
810 if let (Some(definition1), Some(definition2)) = (&resize_data.definition1, &resize_data.definition2) {
811 let actual_length1 = Self::get_actual_length(definition1);
812 let actual_length2 = Self::get_actual_length(definition2);
813 let pixel_length = 1.0 / resize_data.scaling;
814 let epsilon = pixel_length + LayoutHelper::LAYOUT_EPSILON;
815
816 if resize_data.split_behavior == SplitBehavior::Split
821 && !MathUtilities::are_close_eps(
822 actual_length1 + actual_length2,
823 resize_data.original_definition1_actual_length + resize_data.original_definition2_actual_length,
824 epsilon,
825 )
826 {
827 self.cancel_resize();
828
829 return;
830 }
831
832 let (min, max) = Self::get_delta_constraints(&resize_data);
833
834 delta = f64::min(f64::max(delta, min), max);
836
837 let definition1_length_new = actual_length1 + delta;
838 let definition2_length_new = actual_length1 + actual_length2 - definition1_length_new;
839
840 Self::set_lengths(&resize_data, definition1_length_new, definition2_length_new);
841 }
842 }
843
844 fn keyboard_move_splitter(&self, horizontal_change: f64, vertical_change: f64) -> bool {
846 if self.has_resize_data() {
848 return false; }
850
851 self.initialize_data(false);
853
854 if !self.has_resize_data() {
856 return false; }
858
859 self.move_splitter(horizontal_change, vertical_change);
860
861 *self.resize_data.borrow_mut() = None;
862
863 true
864 }
865}
866
867#[repr(C)]
870pub(crate) struct PreviewAdorner {
871 base: Decorator,
872 translation: Ref<TranslateTransform>,
873}
874
875ferro_class!(PreviewAdorner: Decorator);
876ferro_impl_classes!(
877 PreviewAdorner: FerroObjectImpl,
878 StyledElementImpl,
879 VisualImpl,
880 InteractiveImpl,
881 InputElementImpl,
882 ControlImpl
883);
884
885impl LayoutableImpl for PreviewAdorner {
886 fn arrange_override(this: &Self, final_size: Size) -> Size {
887 this.set_clip(None::<Ref<Geometry>>);
891
892 Self::parent_arrange_override(this, final_size)
893 }
894}
895
896impl PreviewAdorner {
897 fn new(preview_control: Option<Ref<Control>>) -> Ref<Self> {
898 let translation = TranslateTransform::new();
900 let this = instantiate(Self { base: Decorator::construct(), translation: translation.clone() });
901
902 let decorator = Decorator::new();
903 decorator.set_child(preview_control);
904 let transform: Rc<dyn ITransform> = translation.into();
905 decorator.set_render_transform(Some(transform));
906
907 this.set_child(decorator);
908 this
909 }
910
911 fn offset_x(&self) -> f64 {
913 self.translation.x()
914 }
915
916 fn set_offset_x(&self, value: f64) {
917 self.translation.set_x(value)
918 }
919
920 fn offset_y(&self) -> f64 {
922 self.translation.y()
923 }
924
925 fn set_offset_y(&self, value: f64) {
926 self.translation.set_y(value)
927 }
928}