1use alloc::collections::btree_map::BTreeMap;
14use alloc::vec::Vec;
15
16use crate::dom::{DomId, DomNodeId, NodeId, OptionDomNodeId};
17use crate::geom::LogicalPosition;
18use crate::selection::TextCursor;
19use crate::window::WindowPosition;
20
21use azul_css::{AzString, OptionString, StringVec};
22
23#[derive(Debug, Clone, PartialEq)]
28#[repr(C, u8)]
29pub enum ActiveDragType {
30 TextSelection(TextSelectionDrag),
32 ScrollbarThumb(ScrollbarThumbDrag),
34 Node(NodeDrag),
36 WindowMove(WindowMoveDrag),
38 WindowResize(WindowResizeDrag),
40 FileDrop(FileDropDrag),
42}
43
44#[derive(Debug, Clone, PartialEq)]
48#[repr(C)]
49pub struct TextSelectionDrag {
50 pub dom_id: DomId,
52 pub anchor_ifc_node: NodeId,
54 pub anchor_cursor: Option<TextCursor>,
56 pub start_mouse_position: LogicalPosition,
58 pub current_mouse_position: LogicalPosition,
60 pub auto_scroll_direction: AutoScrollDirection,
62 pub auto_scroll_container: Option<NodeId>,
64}
65
66#[derive(Debug, Clone, Copy, PartialEq)]
70#[repr(C)]
71pub struct ScrollbarThumbDrag {
72 pub scroll_container_node: NodeId,
74 pub axis: ScrollbarAxis,
76 pub start_mouse_position: LogicalPosition,
78 pub start_scroll_offset: f32,
80 pub current_mouse_position: LogicalPosition,
82 pub track_length_px: f32,
84 pub content_length_px: f32,
86 pub viewport_length_px: f32,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92#[repr(C)]
93pub enum ScrollbarAxis {
94 Vertical,
95 Horizontal,
96}
97
98#[derive(Debug, Clone, PartialEq)]
102#[repr(C)]
103pub struct NodeDrag {
104 pub dom_id: DomId,
106 pub node_id: NodeId,
108 pub start_position: LogicalPosition,
110 pub current_position: LogicalPosition,
112 pub current_drop_target: OptionDomNodeId,
114 pub drag_data: DragData,
116}
117
118#[derive(Debug, Clone, PartialEq)]
122#[repr(C)]
123pub struct WindowMoveDrag {
124 pub start_position: LogicalPosition,
126 pub current_position: LogicalPosition,
128 pub initial_window_position: WindowPosition,
130}
131
132#[derive(Debug, Clone, Copy, PartialEq)]
136#[repr(C)]
137pub struct WindowResizeDrag {
138 pub edge: WindowResizeEdge,
140 pub start_position: LogicalPosition,
142 pub current_position: LogicalPosition,
144 pub initial_width: u32,
146 pub initial_height: u32,
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
152#[repr(C)]
153pub enum WindowResizeEdge {
154 Top,
155 Bottom,
156 Left,
157 Right,
158 TopLeft,
159 TopRight,
160 BottomLeft,
161 BottomRight,
162}
163
164#[derive(Debug, Clone, PartialEq)]
168#[repr(C)]
169pub struct FileDropDrag {
170 pub files: StringVec,
172 pub position: LogicalPosition,
174 pub drop_target: OptionDomNodeId,
176 pub drop_effect: DropEffect,
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
182#[repr(C)]
183pub enum AutoScrollDirection {
184 #[default]
186 None,
187 Up,
189 Down,
191 Left,
193 Right,
195 UpLeft,
197 UpRight,
199 DownLeft,
201 DownRight,
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
207#[repr(C)]
208pub enum DropEffect {
209 #[default]
211 None,
212 Copy,
214 Move,
216 Link,
218}
219
220#[derive(Debug, Clone, PartialEq, Default)]
222pub struct DragData {
223 pub data: BTreeMap<AzString, Vec<u8>>,
227 pub effect_allowed: DragEffect,
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
233#[repr(C)]
234pub enum DragEffect {
235 #[default]
237 None,
238 Copy,
240 Move,
242 Link,
244}
245
246impl DragData {
247 pub fn new() -> Self {
249 Self {
250 data: BTreeMap::new(),
251 effect_allowed: DragEffect::Copy,
252 }
253 }
254
255 pub fn set_data(&mut self, mime_type: impl Into<AzString>, data: Vec<u8>) {
257 self.data.insert(mime_type.into(), data);
258 }
259
260 pub fn get_data(&self, mime_type: &str) -> Option<&[u8]> {
262 self.data.get(&AzString::from(mime_type)).map(|v| v.as_slice())
263 }
264
265 pub fn set_text(&mut self, text: impl Into<AzString>) {
267 let text_str = text.into();
268 self.set_data("text/plain", text_str.as_str().as_bytes().to_vec());
269 }
270
271 pub fn get_text(&self) -> Option<AzString> {
273 self.get_data("text/plain")
274 .map(|bytes| AzString::from(core::str::from_utf8(bytes).unwrap_or("")))
275 }
276}
277
278#[derive(Debug, Clone, PartialEq)]
283pub struct DragContext {
284 pub drag_type: ActiveDragType,
286 pub session_id: u64,
288 pub cancelled: bool,
290}
291
292impl DragContext {
293 pub fn new(drag_type: ActiveDragType, session_id: u64) -> Self {
295 Self {
296 drag_type,
297 session_id,
298 cancelled: false,
299 }
300 }
301
302 pub fn text_selection(
304 dom_id: DomId,
305 anchor_ifc_node: NodeId,
306 start_mouse_position: LogicalPosition,
307 session_id: u64,
308 ) -> Self {
309 Self::new(
310 ActiveDragType::TextSelection(TextSelectionDrag {
311 dom_id,
312 anchor_ifc_node,
313 anchor_cursor: None,
314 start_mouse_position,
315 current_mouse_position: start_mouse_position,
316 auto_scroll_direction: AutoScrollDirection::None,
317 auto_scroll_container: None,
318 }),
319 session_id,
320 )
321 }
322
323 pub fn scrollbar_thumb(
325 scroll_container_node: NodeId,
326 axis: ScrollbarAxis,
327 start_mouse_position: LogicalPosition,
328 start_scroll_offset: f32,
329 track_length_px: f32,
330 content_length_px: f32,
331 viewport_length_px: f32,
332 session_id: u64,
333 ) -> Self {
334 Self::new(
335 ActiveDragType::ScrollbarThumb(ScrollbarThumbDrag {
336 scroll_container_node,
337 axis,
338 start_mouse_position,
339 start_scroll_offset,
340 current_mouse_position: start_mouse_position,
341 track_length_px,
342 content_length_px,
343 viewport_length_px,
344 }),
345 session_id,
346 )
347 }
348
349 pub fn node_drag(
351 dom_id: DomId,
352 node_id: NodeId,
353 start_position: LogicalPosition,
354 drag_data: DragData,
355 session_id: u64,
356 ) -> Self {
357 Self::new(
358 ActiveDragType::Node(NodeDrag {
359 dom_id,
360 node_id,
361 start_position,
362 current_position: start_position,
363 current_drop_target: OptionDomNodeId::None,
364 drag_data,
365 }),
366 session_id,
367 )
368 }
369
370 pub fn window_move(
372 start_position: LogicalPosition,
373 initial_window_position: WindowPosition,
374 session_id: u64,
375 ) -> Self {
376 Self::new(
377 ActiveDragType::WindowMove(WindowMoveDrag {
378 start_position,
379 current_position: start_position,
380 initial_window_position,
381 }),
382 session_id,
383 )
384 }
385
386 pub fn file_drop(files: Vec<AzString>, position: LogicalPosition, session_id: u64) -> Self {
388 Self::new(
389 ActiveDragType::FileDrop(FileDropDrag {
390 files: files.into(),
391 position,
392 drop_target: OptionDomNodeId::None,
393 drop_effect: DropEffect::Copy,
394 }),
395 session_id,
396 )
397 }
398
399 pub fn update_position(&mut self, position: LogicalPosition) {
401 match &mut self.drag_type {
402 ActiveDragType::TextSelection(ref mut drag) => {
403 drag.current_mouse_position = position;
404 }
405 ActiveDragType::ScrollbarThumb(ref mut drag) => {
406 drag.current_mouse_position = position;
407 }
408 ActiveDragType::Node(ref mut drag) => {
409 drag.current_position = position;
410 }
411 ActiveDragType::WindowMove(ref mut drag) => {
412 drag.current_position = position;
413 }
414 ActiveDragType::WindowResize(ref mut drag) => {
415 drag.current_position = position;
416 }
417 ActiveDragType::FileDrop(ref mut drag) => {
418 drag.position = position;
419 }
420 }
421 }
422
423 pub fn current_position(&self) -> LogicalPosition {
425 match &self.drag_type {
426 ActiveDragType::TextSelection(drag) => drag.current_mouse_position,
427 ActiveDragType::ScrollbarThumb(drag) => drag.current_mouse_position,
428 ActiveDragType::Node(drag) => drag.current_position,
429 ActiveDragType::WindowMove(drag) => drag.current_position,
430 ActiveDragType::WindowResize(drag) => drag.current_position,
431 ActiveDragType::FileDrop(drag) => drag.position,
432 }
433 }
434
435 pub fn start_position(&self) -> LogicalPosition {
437 match &self.drag_type {
438 ActiveDragType::TextSelection(drag) => drag.start_mouse_position,
439 ActiveDragType::ScrollbarThumb(drag) => drag.start_mouse_position,
440 ActiveDragType::Node(drag) => drag.start_position,
441 ActiveDragType::WindowMove(drag) => drag.start_position,
442 ActiveDragType::WindowResize(drag) => drag.start_position,
443 ActiveDragType::FileDrop(drag) => drag.position, }
445 }
446
447 pub fn is_text_selection(&self) -> bool {
449 matches!(self.drag_type, ActiveDragType::TextSelection(_))
450 }
451
452 pub fn is_scrollbar_thumb(&self) -> bool {
454 matches!(self.drag_type, ActiveDragType::ScrollbarThumb(_))
455 }
456
457 pub fn is_node_drag(&self) -> bool {
459 matches!(self.drag_type, ActiveDragType::Node(_))
460 }
461
462 pub fn is_window_move(&self) -> bool {
464 matches!(self.drag_type, ActiveDragType::WindowMove(_))
465 }
466
467 pub fn is_file_drop(&self) -> bool {
469 matches!(self.drag_type, ActiveDragType::FileDrop(_))
470 }
471
472 pub fn as_text_selection(&self) -> Option<&TextSelectionDrag> {
474 match &self.drag_type {
475 ActiveDragType::TextSelection(drag) => Some(drag),
476 _ => None,
477 }
478 }
479
480 pub fn as_text_selection_mut(&mut self) -> Option<&mut TextSelectionDrag> {
482 match &mut self.drag_type {
483 ActiveDragType::TextSelection(drag) => Some(drag),
484 _ => None,
485 }
486 }
487
488 pub fn as_scrollbar_thumb(&self) -> Option<&ScrollbarThumbDrag> {
490 match &self.drag_type {
491 ActiveDragType::ScrollbarThumb(drag) => Some(drag),
492 _ => None,
493 }
494 }
495
496 pub fn as_scrollbar_thumb_mut(&mut self) -> Option<&mut ScrollbarThumbDrag> {
498 match &mut self.drag_type {
499 ActiveDragType::ScrollbarThumb(drag) => Some(drag),
500 _ => None,
501 }
502 }
503
504 pub fn as_node_drag(&self) -> Option<&NodeDrag> {
506 match &self.drag_type {
507 ActiveDragType::Node(drag) => Some(drag),
508 _ => None,
509 }
510 }
511
512 pub fn as_node_drag_mut(&mut self) -> Option<&mut NodeDrag> {
514 match &mut self.drag_type {
515 ActiveDragType::Node(drag) => Some(drag),
516 _ => None,
517 }
518 }
519
520 pub fn as_window_move(&self) -> Option<&WindowMoveDrag> {
522 match &self.drag_type {
523 ActiveDragType::WindowMove(drag) => Some(drag),
524 _ => None,
525 }
526 }
527
528 pub fn as_file_drop(&self) -> Option<&FileDropDrag> {
530 match &self.drag_type {
531 ActiveDragType::FileDrop(drag) => Some(drag),
532 _ => None,
533 }
534 }
535
536 pub fn as_file_drop_mut(&mut self) -> Option<&mut FileDropDrag> {
538 match &mut self.drag_type {
539 ActiveDragType::FileDrop(drag) => Some(drag),
540 _ => None,
541 }
542 }
543
544 pub fn calculate_scrollbar_scroll_offset(&self) -> Option<f32> {
548 let drag = self.as_scrollbar_thumb()?;
549
550 let mouse_delta = match drag.axis {
552 ScrollbarAxis::Vertical => {
553 drag.current_mouse_position.y - drag.start_mouse_position.y
554 }
555 ScrollbarAxis::Horizontal => {
556 drag.current_mouse_position.x - drag.start_mouse_position.x
557 }
558 };
559
560 let scrollable_range = drag.content_length_px - drag.viewport_length_px;
562 if scrollable_range <= 0.0 || drag.track_length_px <= 0.0 {
563 return Some(drag.start_scroll_offset);
564 }
565
566 let thumb_length = (drag.viewport_length_px / drag.content_length_px) * drag.track_length_px;
568 let scrollable_track = drag.track_length_px - thumb_length;
569
570 if scrollable_track <= 0.0 {
571 return Some(drag.start_scroll_offset);
572 }
573
574 let scroll_ratio = mouse_delta / scrollable_track;
576 let scroll_delta = scroll_ratio * scrollable_range;
577
578 let new_offset = drag.start_scroll_offset + scroll_delta;
580
581 Some(new_offset.clamp(0.0, scrollable_range))
583 }
584}
585
586azul_css::impl_option!(
587 DragContext,
588 OptionDragContext,
589 copy = false,
590 [Debug, Clone, PartialEq]
591);