1use alloc::vec::Vec;
14
15use crate::dom::{DomId, DomNodeId, NodeId, OptionDomNodeId};
16use crate::geom::LogicalPosition;
17use crate::selection::TextCursor;
18use crate::window::WindowPosition;
19
20use azul_css::{AzString, StringVec, U8Vec};
21
22#[derive(Debug, Clone, PartialEq)]
27#[repr(C, u8)]
28pub enum ActiveDragType {
29 TextSelection(TextSelectionDrag),
31 ScrollbarThumb(ScrollbarThumbDrag),
33 Node(NodeDrag),
35 WindowMove(WindowMoveDrag),
37 WindowResize(WindowResizeDrag),
39 FileDrop(FileDropDrag),
41}
42
43#[derive(Debug, Clone, PartialEq)]
47#[repr(C)]
48pub struct TextSelectionDrag {
49 pub dom_id: DomId,
51 pub anchor_ifc_node: NodeId,
53 pub anchor_cursor: Option<TextCursor>,
55 pub start_mouse_position: LogicalPosition,
57 pub current_mouse_position: LogicalPosition,
59 pub auto_scroll_direction: AutoScrollDirection,
61 pub auto_scroll_container: Option<NodeId>,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq)]
69#[repr(C)]
70pub struct ScrollbarThumbDrag {
71 pub scroll_container_node: NodeId,
73 pub axis: ScrollbarAxis,
75 pub start_mouse_position: LogicalPosition,
77 pub start_scroll_offset: f32,
79 pub current_mouse_position: LogicalPosition,
81 pub track_length_px: f32,
83 pub content_length_px: f32,
85 pub viewport_length_px: f32,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91#[repr(C)]
92pub enum ScrollbarAxis {
93 Vertical,
94 Horizontal,
95}
96
97#[derive(Debug, Clone, PartialEq)]
101#[repr(C)]
102pub struct NodeDrag {
103 pub dom_id: DomId,
105 pub node_id: NodeId,
107 pub start_position: LogicalPosition,
109 pub current_position: LogicalPosition,
111 pub drag_offset: LogicalPosition,
113 pub current_drop_target: OptionDomNodeId,
115 pub previous_drop_target: OptionDomNodeId,
117 pub drag_data: DragData,
119 pub drop_accepted: bool,
121 pub drop_effect: DropEffect,
123}
124
125#[derive(Debug, Clone, PartialEq)]
129#[repr(C)]
130pub struct WindowMoveDrag {
131 pub start_position: LogicalPosition,
133 pub current_position: LogicalPosition,
135 pub initial_window_position: WindowPosition,
137}
138
139#[derive(Debug, Clone, Copy, PartialEq)]
143#[repr(C)]
144pub struct WindowResizeDrag {
145 pub edge: WindowResizeEdge,
147 pub start_position: LogicalPosition,
149 pub current_position: LogicalPosition,
151 pub initial_width: u32,
153 pub initial_height: u32,
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159#[repr(C)]
160pub enum WindowResizeEdge {
161 Top,
162 Bottom,
163 Left,
164 Right,
165 TopLeft,
166 TopRight,
167 BottomLeft,
168 BottomRight,
169}
170
171#[derive(Debug, Clone, PartialEq)]
175#[repr(C)]
176pub struct FileDropDrag {
177 pub files: StringVec,
179 pub position: LogicalPosition,
181 pub drop_target: OptionDomNodeId,
183 pub drop_effect: DropEffect,
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
189#[repr(C)]
190pub enum AutoScrollDirection {
191 #[default]
192 None,
193 Up,
194 Down,
195 Left,
196 Right,
197 UpLeft,
198 UpRight,
199 DownLeft,
200 DownRight,
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
210#[repr(C)]
211pub enum DropEffect {
212 #[default]
214 None,
215 Copy,
217 Link,
219 Move,
221}
222
223#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
231#[repr(C)]
232pub enum DragEffect {
233 #[default]
236 Uninitialized,
237 None,
239 Copy,
241 CopyLink,
243 CopyMove,
245 Link,
247 LinkMove,
249 Move,
251 All,
253}
254
255#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
258#[repr(C)]
259pub struct MimeTypeData {
260 pub mime_type: AzString,
261 pub data: U8Vec,
262}
263
264impl_option!(
265 MimeTypeData,
266 OptionMimeTypeData,
267 copy = false,
268 [Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash]
269);
270
271impl_vec!(
272 MimeTypeData,
273 MimeTypeDataVec,
274 MimeTypeDataVecDestructor,
275 MimeTypeDataVecDestructorType,
276 MimeTypeDataVecSlice,
277 OptionMimeTypeData
278);
279impl_vec_mut!(MimeTypeData, MimeTypeDataVec);
280impl_vec_debug!(MimeTypeData, MimeTypeDataVec);
281impl_vec_partialord!(MimeTypeData, MimeTypeDataVec);
282impl_vec_ord!(MimeTypeData, MimeTypeDataVec);
283impl_vec_clone!(MimeTypeData, MimeTypeDataVec, MimeTypeDataVecDestructor);
284impl_vec_partialeq!(MimeTypeData, MimeTypeDataVec);
285impl_vec_eq!(MimeTypeData, MimeTypeDataVec);
286impl_vec_hash!(MimeTypeData, MimeTypeDataVec);
287
288#[derive(Debug, Default, Clone, PartialEq)]
293#[repr(C)]
294pub struct DragData {
295 pub data: MimeTypeDataVec,
299 pub effect_allowed: DragEffect,
301}
302
303impl DragData {
304 pub fn new() -> Self {
306 Self {
307 data: MimeTypeDataVec::new(),
308 effect_allowed: DragEffect::Uninitialized,
309 }
310 }
311
312 pub fn set_data(&mut self, mime_type: impl Into<AzString>, data: Vec<u8>) {
315 let mime_type = mime_type.into();
316 let value: U8Vec = data.into();
317 if let Some(entry) = self
318 .data
319 .as_mut()
320 .iter_mut()
321 .find(|e| e.mime_type == mime_type)
322 {
323 entry.data = value;
324 } else {
325 self.data.push(MimeTypeData {
326 mime_type,
327 data: value,
328 });
329 }
330 }
331
332 pub fn get_data(&self, mime_type: &str) -> Option<&[u8]> {
334 self.data
335 .as_ref()
336 .iter()
337 .find(|e| e.mime_type.as_str() == mime_type)
338 .map(|e| e.data.as_ref())
339 }
340
341 pub fn set_text(&mut self, text: impl Into<AzString>) {
343 let text_str = text.into();
344 self.set_data("text/plain", text_str.as_str().as_bytes().to_vec());
345 }
346
347 pub fn get_text(&self) -> Option<AzString> {
349 self.get_data("text/plain")
350 .map(|bytes| AzString::from(core::str::from_utf8(bytes).unwrap_or("")))
351 }
352}
353
354#[derive(Debug, Clone, PartialEq)]
361pub struct DragContext {
362 pub drag_type: ActiveDragType,
364 pub session_id: u64,
366 pub cancelled: bool,
368}
369
370impl DragContext {
371 pub fn new(drag_type: ActiveDragType, session_id: u64) -> Self {
373 Self {
374 drag_type,
375 session_id,
376 cancelled: false,
377 }
378 }
379
380 pub fn text_selection(
382 dom_id: DomId,
383 anchor_ifc_node: NodeId,
384 start_mouse_position: LogicalPosition,
385 session_id: u64,
386 ) -> Self {
387 Self::new(
388 ActiveDragType::TextSelection(TextSelectionDrag {
389 dom_id,
390 anchor_ifc_node,
391 anchor_cursor: None,
392 start_mouse_position,
393 current_mouse_position: start_mouse_position,
394 auto_scroll_direction: AutoScrollDirection::None,
395 auto_scroll_container: None,
396 }),
397 session_id,
398 )
399 }
400
401 pub fn scrollbar_thumb(
403 scroll_container_node: NodeId,
404 axis: ScrollbarAxis,
405 start_mouse_position: LogicalPosition,
406 start_scroll_offset: f32,
407 track_length_px: f32,
408 content_length_px: f32,
409 viewport_length_px: f32,
410 session_id: u64,
411 ) -> Self {
412 Self::new(
413 ActiveDragType::ScrollbarThumb(ScrollbarThumbDrag {
414 scroll_container_node,
415 axis,
416 start_mouse_position,
417 start_scroll_offset,
418 current_mouse_position: start_mouse_position,
419 track_length_px,
420 content_length_px,
421 viewport_length_px,
422 }),
423 session_id,
424 )
425 }
426
427 pub fn node_drag(
429 dom_id: DomId,
430 node_id: NodeId,
431 start_position: LogicalPosition,
432 drag_data: DragData,
433 session_id: u64,
434 ) -> Self {
435 Self::new(
436 ActiveDragType::Node(NodeDrag {
437 dom_id,
438 node_id,
439 start_position,
440 current_position: start_position,
441 drag_offset: LogicalPosition::zero(),
442 current_drop_target: OptionDomNodeId::None,
443 previous_drop_target: OptionDomNodeId::None,
444 drag_data,
445 drop_accepted: false,
446 drop_effect: DropEffect::None,
447 }),
448 session_id,
449 )
450 }
451
452 pub fn window_move(
454 start_position: LogicalPosition,
455 initial_window_position: WindowPosition,
456 session_id: u64,
457 ) -> Self {
458 Self::new(
459 ActiveDragType::WindowMove(WindowMoveDrag {
460 start_position,
461 current_position: start_position,
462 initial_window_position,
463 }),
464 session_id,
465 )
466 }
467
468 pub fn file_drop(files: Vec<AzString>, position: LogicalPosition, session_id: u64) -> Self {
470 Self::new(
471 ActiveDragType::FileDrop(FileDropDrag {
472 files: files.into(),
473 position,
474 drop_target: OptionDomNodeId::None,
475 drop_effect: DropEffect::Copy,
476 }),
477 session_id,
478 )
479 }
480
481 pub fn update_position(&mut self, position: LogicalPosition) {
483 match &mut self.drag_type {
484 ActiveDragType::TextSelection(ref mut drag) => {
485 drag.current_mouse_position = position;
486 }
487 ActiveDragType::ScrollbarThumb(ref mut drag) => {
488 drag.current_mouse_position = position;
489 }
490 ActiveDragType::Node(ref mut drag) => {
491 drag.current_position = position;
492 }
493 ActiveDragType::WindowMove(ref mut drag) => {
494 drag.current_position = position;
495 }
496 ActiveDragType::WindowResize(ref mut drag) => {
497 drag.current_position = position;
498 }
499 ActiveDragType::FileDrop(ref mut drag) => {
500 drag.position = position;
501 }
502 }
503 }
504
505 pub fn current_position(&self) -> LogicalPosition {
507 match &self.drag_type {
508 ActiveDragType::TextSelection(drag) => drag.current_mouse_position,
509 ActiveDragType::ScrollbarThumb(drag) => drag.current_mouse_position,
510 ActiveDragType::Node(drag) => drag.current_position,
511 ActiveDragType::WindowMove(drag) => drag.current_position,
512 ActiveDragType::WindowResize(drag) => drag.current_position,
513 ActiveDragType::FileDrop(drag) => drag.position,
514 }
515 }
516
517 pub fn start_position(&self) -> LogicalPosition {
519 match &self.drag_type {
520 ActiveDragType::TextSelection(drag) => drag.start_mouse_position,
521 ActiveDragType::ScrollbarThumb(drag) => drag.start_mouse_position,
522 ActiveDragType::Node(drag) => drag.start_position,
523 ActiveDragType::WindowMove(drag) => drag.start_position,
524 ActiveDragType::WindowResize(drag) => drag.start_position,
525 ActiveDragType::FileDrop(drag) => drag.position, }
527 }
528
529 pub fn is_text_selection(&self) -> bool {
531 matches!(self.drag_type, ActiveDragType::TextSelection(_))
532 }
533
534 pub fn is_scrollbar_thumb(&self) -> bool {
536 matches!(self.drag_type, ActiveDragType::ScrollbarThumb(_))
537 }
538
539 pub fn is_node_drag(&self) -> bool {
541 matches!(self.drag_type, ActiveDragType::Node(_))
542 }
543
544 pub fn is_window_move(&self) -> bool {
546 matches!(self.drag_type, ActiveDragType::WindowMove(_))
547 }
548
549 pub fn is_file_drop(&self) -> bool {
551 matches!(self.drag_type, ActiveDragType::FileDrop(_))
552 }
553
554 pub fn as_text_selection(&self) -> Option<&TextSelectionDrag> {
556 match &self.drag_type {
557 ActiveDragType::TextSelection(drag) => Some(drag),
558 _ => None,
559 }
560 }
561
562 pub fn as_text_selection_mut(&mut self) -> Option<&mut TextSelectionDrag> {
564 match &mut self.drag_type {
565 ActiveDragType::TextSelection(drag) => Some(drag),
566 _ => None,
567 }
568 }
569
570 pub fn as_scrollbar_thumb(&self) -> Option<&ScrollbarThumbDrag> {
572 match &self.drag_type {
573 ActiveDragType::ScrollbarThumb(drag) => Some(drag),
574 _ => None,
575 }
576 }
577
578 pub fn as_scrollbar_thumb_mut(&mut self) -> Option<&mut ScrollbarThumbDrag> {
580 match &mut self.drag_type {
581 ActiveDragType::ScrollbarThumb(drag) => Some(drag),
582 _ => None,
583 }
584 }
585
586 pub fn as_node_drag(&self) -> Option<&NodeDrag> {
588 match &self.drag_type {
589 ActiveDragType::Node(drag) => Some(drag),
590 _ => None,
591 }
592 }
593
594 pub fn as_node_drag_mut(&mut self) -> Option<&mut NodeDrag> {
596 match &mut self.drag_type {
597 ActiveDragType::Node(drag) => Some(drag),
598 _ => None,
599 }
600 }
601
602 pub fn as_window_move(&self) -> Option<&WindowMoveDrag> {
604 match &self.drag_type {
605 ActiveDragType::WindowMove(drag) => Some(drag),
606 _ => None,
607 }
608 }
609
610 pub fn as_file_drop(&self) -> Option<&FileDropDrag> {
612 match &self.drag_type {
613 ActiveDragType::FileDrop(drag) => Some(drag),
614 _ => None,
615 }
616 }
617
618 pub fn as_file_drop_mut(&mut self) -> Option<&mut FileDropDrag> {
620 match &mut self.drag_type {
621 ActiveDragType::FileDrop(drag) => Some(drag),
622 _ => None,
623 }
624 }
625
626 pub fn calculate_scrollbar_scroll_offset(&self) -> Option<f32> {
630 let drag = self.as_scrollbar_thumb()?;
631
632 let mouse_delta = match drag.axis {
634 ScrollbarAxis::Vertical => {
635 drag.current_mouse_position.y - drag.start_mouse_position.y
636 }
637 ScrollbarAxis::Horizontal => {
638 drag.current_mouse_position.x - drag.start_mouse_position.x
639 }
640 };
641
642 let scrollable_range = drag.content_length_px - drag.viewport_length_px;
644 if scrollable_range <= 0.0 || drag.track_length_px <= 0.0 {
645 return Some(drag.start_scroll_offset);
646 }
647
648 let thumb_length = (drag.viewport_length_px / drag.content_length_px) * drag.track_length_px;
650 let scrollable_track = drag.track_length_px - thumb_length;
651
652 if scrollable_track <= 0.0 {
653 return Some(drag.start_scroll_offset);
654 }
655
656 let scroll_ratio = mouse_delta / scrollable_track;
658 let scroll_delta = scroll_ratio * scrollable_range;
659
660 let new_offset = drag.start_scroll_offset + scroll_delta;
662
663 Some(new_offset.clamp(0.0, scrollable_range))
665 }
666
667 fn remap_drop_target(
670 target: &mut OptionDomNodeId,
671 dom_id: DomId,
672 node_id_map: &alloc::collections::BTreeMap<NodeId, NodeId>,
673 ) {
674 let dt = match target.into_option() {
675 Some(dt) if dt.dom == dom_id => dt,
676 _ => return,
677 };
678 let old_nid = match dt.node.into_crate_internal() {
679 Some(nid) => nid,
680 None => return,
681 };
682 if let Some(&new_nid) = node_id_map.get(&old_nid) {
683 *target = Some(DomNodeId {
684 dom: dom_id,
685 node: crate::styled_dom::NodeHierarchyItemId::from_crate_internal(Some(new_nid)),
686 }).into();
687 } else {
688 *target = OptionDomNodeId::None;
689 }
690 }
691
692 pub fn remap_node_ids(
698 &mut self,
699 dom_id: DomId,
700 node_id_map: &alloc::collections::BTreeMap<NodeId, NodeId>,
701 ) -> bool {
702 match &mut self.drag_type {
703 ActiveDragType::TextSelection(ref mut drag) => {
704 if drag.dom_id != dom_id {
705 return true;
706 }
707 if let Some(&new_id) = node_id_map.get(&drag.anchor_ifc_node) {
708 drag.anchor_ifc_node = new_id;
709 } else {
710 return false; }
712 if let Some(ref mut container) = drag.auto_scroll_container {
713 if let Some(&new_id) = node_id_map.get(container) {
714 *container = new_id;
715 } else {
716 drag.auto_scroll_container = None;
717 }
718 }
719 true
720 }
721 ActiveDragType::ScrollbarThumb(ref mut drag) => {
722 if let Some(&new_id) = node_id_map.get(&drag.scroll_container_node) {
723 drag.scroll_container_node = new_id;
724 true
725 } else {
726 false }
728 }
729 ActiveDragType::Node(ref mut drag) => {
730 if drag.dom_id != dom_id {
731 return true;
732 }
733 if let Some(&new_id) = node_id_map.get(&drag.node_id) {
734 drag.node_id = new_id;
735 } else {
736 return false; }
738 Self::remap_drop_target(&mut drag.current_drop_target, dom_id, node_id_map);
740 true
741 }
742 ActiveDragType::WindowMove(_) | ActiveDragType::WindowResize(_) => true,
744 ActiveDragType::FileDrop(ref mut drag) => {
745 Self::remap_drop_target(&mut drag.drop_target, dom_id, node_id_map);
746 true
747 }
748 }
749 }
750}
751
752azul_css::impl_option!(
753 DragContext,
754 OptionDragContext,
755 copy = false,
756 [Debug, Clone, PartialEq]
757);
758
759
760#[derive(Default, Debug, Copy, Clone, PartialEq, PartialOrd)]
763#[repr(C)]
764pub struct DragDelta {
765 pub dx: f32,
766 pub dy: f32,
767}
768
769impl DragDelta {
770 #[inline(always)]
771 pub const fn new(dx: f32, dy: f32) -> Self {
772 Self { dx, dy }
773 }
774 #[inline(always)]
775 pub const fn zero() -> Self {
776 Self::new(0.0, 0.0)
777 }
778}
779
780impl_option!(
781 DragDelta,
782 OptionDragDelta,
783 [Debug, Copy, Clone, PartialEq, PartialOrd]
784);