use alloc::vec::Vec;
use crate::dom::{DomId, DomNodeId, NodeId, OptionDomNodeId};
use crate::geom::LogicalPosition;
use crate::selection::TextCursor;
use crate::window::WindowPosition;
use azul_css::{AzString, StringVec, U8Vec};
#[derive(Debug, Clone, PartialEq)]
#[repr(C, u8)]
pub enum ActiveDragType {
TextSelection(TextSelectionDrag),
ScrollbarThumb(ScrollbarThumbDrag),
Node(NodeDrag),
WindowMove(WindowMoveDrag),
WindowResize(WindowResizeDrag),
FileDrop(FileDropDrag),
}
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub struct TextSelectionDrag {
pub dom_id: DomId,
pub anchor_ifc_node: NodeId,
pub anchor_cursor: Option<TextCursor>,
pub start_mouse_position: LogicalPosition,
pub current_mouse_position: LogicalPosition,
pub auto_scroll_direction: AutoScrollDirection,
pub auto_scroll_container: Option<NodeId>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct ScrollbarThumbDrag {
pub scroll_container_node: NodeId,
pub axis: ScrollbarAxis,
pub start_mouse_position: LogicalPosition,
pub start_scroll_offset: f32,
pub current_mouse_position: LogicalPosition,
pub track_length_px: f32,
pub content_length_px: f32,
pub viewport_length_px: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum ScrollbarAxis {
Vertical,
Horizontal,
}
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub struct NodeDrag {
pub dom_id: DomId,
pub node_id: NodeId,
pub start_position: LogicalPosition,
pub current_position: LogicalPosition,
pub drag_offset: LogicalPosition,
pub current_drop_target: OptionDomNodeId,
pub previous_drop_target: OptionDomNodeId,
pub drag_data: DragData,
pub drop_accepted: bool,
pub drop_effect: DropEffect,
}
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub struct WindowMoveDrag {
pub start_position: LogicalPosition,
pub current_position: LogicalPosition,
pub initial_window_position: WindowPosition,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct WindowResizeDrag {
pub edge: WindowResizeEdge,
pub start_position: LogicalPosition,
pub current_position: LogicalPosition,
pub initial_width: u32,
pub initial_height: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum WindowResizeEdge {
Top,
Bottom,
Left,
Right,
TopLeft,
TopRight,
BottomLeft,
BottomRight,
}
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub struct FileDropDrag {
pub files: StringVec,
pub position: LogicalPosition,
pub drop_target: OptionDomNodeId,
pub drop_effect: DropEffect,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(C)]
pub enum AutoScrollDirection {
#[default]
None,
Up,
Down,
Left,
Right,
UpLeft,
UpRight,
DownLeft,
DownRight,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(C)]
pub enum DropEffect {
#[default]
None,
Copy,
Link,
Move,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(C)]
pub enum DragEffect {
#[default]
Uninitialized,
None,
Copy,
CopyLink,
CopyMove,
Link,
LinkMove,
Move,
All,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct MimeTypeData {
pub mime_type: AzString,
pub data: U8Vec,
}
impl_option!(
MimeTypeData,
OptionMimeTypeData,
copy = false,
[Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash]
);
impl_vec!(
MimeTypeData,
MimeTypeDataVec,
MimeTypeDataVecDestructor,
MimeTypeDataVecDestructorType,
MimeTypeDataVecSlice,
OptionMimeTypeData
);
impl_vec_mut!(MimeTypeData, MimeTypeDataVec);
impl_vec_debug!(MimeTypeData, MimeTypeDataVec);
impl_vec_partialord!(MimeTypeData, MimeTypeDataVec);
impl_vec_ord!(MimeTypeData, MimeTypeDataVec);
impl_vec_clone!(MimeTypeData, MimeTypeDataVec, MimeTypeDataVecDestructor);
impl_vec_partialeq!(MimeTypeData, MimeTypeDataVec);
impl_vec_eq!(MimeTypeData, MimeTypeDataVec);
impl_vec_hash!(MimeTypeData, MimeTypeDataVec);
#[derive(Debug, Default, Clone, PartialEq)]
#[repr(C)]
pub struct DragData {
pub data: MimeTypeDataVec,
pub effect_allowed: DragEffect,
}
impl DragData {
pub fn new() -> Self {
Self {
data: MimeTypeDataVec::new(),
effect_allowed: DragEffect::Uninitialized,
}
}
pub fn set_data(&mut self, mime_type: impl Into<AzString>, data: Vec<u8>) {
let mime_type = mime_type.into();
let value: U8Vec = data.into();
if let Some(entry) = self
.data
.as_mut()
.iter_mut()
.find(|e| e.mime_type == mime_type)
{
entry.data = value;
} else {
self.data.push(MimeTypeData {
mime_type,
data: value,
});
}
}
pub fn get_data(&self, mime_type: &str) -> Option<&[u8]> {
self.data
.as_ref()
.iter()
.find(|e| e.mime_type.as_str() == mime_type)
.map(|e| e.data.as_ref())
}
pub fn set_text(&mut self, text: impl Into<AzString>) {
let text_str = text.into();
self.set_data("text/plain", text_str.as_str().as_bytes().to_vec());
}
pub fn get_text(&self) -> Option<AzString> {
self.get_data("text/plain")
.map(|bytes| AzString::from(core::str::from_utf8(bytes).unwrap_or("")))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DragContext {
pub drag_type: ActiveDragType,
pub session_id: u64,
pub cancelled: bool,
}
impl DragContext {
pub fn new(drag_type: ActiveDragType, session_id: u64) -> Self {
Self {
drag_type,
session_id,
cancelled: false,
}
}
pub fn text_selection(
dom_id: DomId,
anchor_ifc_node: NodeId,
start_mouse_position: LogicalPosition,
session_id: u64,
) -> Self {
Self::new(
ActiveDragType::TextSelection(TextSelectionDrag {
dom_id,
anchor_ifc_node,
anchor_cursor: None,
start_mouse_position,
current_mouse_position: start_mouse_position,
auto_scroll_direction: AutoScrollDirection::None,
auto_scroll_container: None,
}),
session_id,
)
}
pub fn scrollbar_thumb(
scroll_container_node: NodeId,
axis: ScrollbarAxis,
start_mouse_position: LogicalPosition,
start_scroll_offset: f32,
track_length_px: f32,
content_length_px: f32,
viewport_length_px: f32,
session_id: u64,
) -> Self {
Self::new(
ActiveDragType::ScrollbarThumb(ScrollbarThumbDrag {
scroll_container_node,
axis,
start_mouse_position,
start_scroll_offset,
current_mouse_position: start_mouse_position,
track_length_px,
content_length_px,
viewport_length_px,
}),
session_id,
)
}
pub fn node_drag(
dom_id: DomId,
node_id: NodeId,
start_position: LogicalPosition,
drag_data: DragData,
session_id: u64,
) -> Self {
Self::new(
ActiveDragType::Node(NodeDrag {
dom_id,
node_id,
start_position,
current_position: start_position,
drag_offset: LogicalPosition::zero(),
current_drop_target: OptionDomNodeId::None,
previous_drop_target: OptionDomNodeId::None,
drag_data,
drop_accepted: false,
drop_effect: DropEffect::None,
}),
session_id,
)
}
pub fn window_move(
start_position: LogicalPosition,
initial_window_position: WindowPosition,
session_id: u64,
) -> Self {
Self::new(
ActiveDragType::WindowMove(WindowMoveDrag {
start_position,
current_position: start_position,
initial_window_position,
}),
session_id,
)
}
pub fn file_drop(files: Vec<AzString>, position: LogicalPosition, session_id: u64) -> Self {
Self::new(
ActiveDragType::FileDrop(FileDropDrag {
files: files.into(),
position,
drop_target: OptionDomNodeId::None,
drop_effect: DropEffect::Copy,
}),
session_id,
)
}
pub fn update_position(&mut self, position: LogicalPosition) {
match &mut self.drag_type {
ActiveDragType::TextSelection(ref mut drag) => {
drag.current_mouse_position = position;
}
ActiveDragType::ScrollbarThumb(ref mut drag) => {
drag.current_mouse_position = position;
}
ActiveDragType::Node(ref mut drag) => {
drag.current_position = position;
}
ActiveDragType::WindowMove(ref mut drag) => {
drag.current_position = position;
}
ActiveDragType::WindowResize(ref mut drag) => {
drag.current_position = position;
}
ActiveDragType::FileDrop(ref mut drag) => {
drag.position = position;
}
}
}
pub fn current_position(&self) -> LogicalPosition {
match &self.drag_type {
ActiveDragType::TextSelection(drag) => drag.current_mouse_position,
ActiveDragType::ScrollbarThumb(drag) => drag.current_mouse_position,
ActiveDragType::Node(drag) => drag.current_position,
ActiveDragType::WindowMove(drag) => drag.current_position,
ActiveDragType::WindowResize(drag) => drag.current_position,
ActiveDragType::FileDrop(drag) => drag.position,
}
}
pub fn start_position(&self) -> LogicalPosition {
match &self.drag_type {
ActiveDragType::TextSelection(drag) => drag.start_mouse_position,
ActiveDragType::ScrollbarThumb(drag) => drag.start_mouse_position,
ActiveDragType::Node(drag) => drag.start_position,
ActiveDragType::WindowMove(drag) => drag.start_position,
ActiveDragType::WindowResize(drag) => drag.start_position,
ActiveDragType::FileDrop(drag) => drag.position, }
}
pub fn is_text_selection(&self) -> bool {
matches!(self.drag_type, ActiveDragType::TextSelection(_))
}
pub fn is_scrollbar_thumb(&self) -> bool {
matches!(self.drag_type, ActiveDragType::ScrollbarThumb(_))
}
pub fn is_node_drag(&self) -> bool {
matches!(self.drag_type, ActiveDragType::Node(_))
}
pub fn is_window_move(&self) -> bool {
matches!(self.drag_type, ActiveDragType::WindowMove(_))
}
pub fn is_file_drop(&self) -> bool {
matches!(self.drag_type, ActiveDragType::FileDrop(_))
}
pub fn as_text_selection(&self) -> Option<&TextSelectionDrag> {
match &self.drag_type {
ActiveDragType::TextSelection(drag) => Some(drag),
_ => None,
}
}
pub fn as_text_selection_mut(&mut self) -> Option<&mut TextSelectionDrag> {
match &mut self.drag_type {
ActiveDragType::TextSelection(drag) => Some(drag),
_ => None,
}
}
pub fn as_scrollbar_thumb(&self) -> Option<&ScrollbarThumbDrag> {
match &self.drag_type {
ActiveDragType::ScrollbarThumb(drag) => Some(drag),
_ => None,
}
}
pub fn as_scrollbar_thumb_mut(&mut self) -> Option<&mut ScrollbarThumbDrag> {
match &mut self.drag_type {
ActiveDragType::ScrollbarThumb(drag) => Some(drag),
_ => None,
}
}
pub fn as_node_drag(&self) -> Option<&NodeDrag> {
match &self.drag_type {
ActiveDragType::Node(drag) => Some(drag),
_ => None,
}
}
pub fn as_node_drag_mut(&mut self) -> Option<&mut NodeDrag> {
match &mut self.drag_type {
ActiveDragType::Node(drag) => Some(drag),
_ => None,
}
}
pub fn as_window_move(&self) -> Option<&WindowMoveDrag> {
match &self.drag_type {
ActiveDragType::WindowMove(drag) => Some(drag),
_ => None,
}
}
pub fn as_file_drop(&self) -> Option<&FileDropDrag> {
match &self.drag_type {
ActiveDragType::FileDrop(drag) => Some(drag),
_ => None,
}
}
pub fn as_file_drop_mut(&mut self) -> Option<&mut FileDropDrag> {
match &mut self.drag_type {
ActiveDragType::FileDrop(drag) => Some(drag),
_ => None,
}
}
pub fn calculate_scrollbar_scroll_offset(&self) -> Option<f32> {
let drag = self.as_scrollbar_thumb()?;
let mouse_delta = match drag.axis {
ScrollbarAxis::Vertical => {
drag.current_mouse_position.y - drag.start_mouse_position.y
}
ScrollbarAxis::Horizontal => {
drag.current_mouse_position.x - drag.start_mouse_position.x
}
};
let scrollable_range = drag.content_length_px - drag.viewport_length_px;
if scrollable_range <= 0.0 || drag.track_length_px <= 0.0 {
return Some(drag.start_scroll_offset);
}
let thumb_length = (drag.viewport_length_px / drag.content_length_px) * drag.track_length_px;
let scrollable_track = drag.track_length_px - thumb_length;
if scrollable_track <= 0.0 {
return Some(drag.start_scroll_offset);
}
let scroll_ratio = mouse_delta / scrollable_track;
let scroll_delta = scroll_ratio * scrollable_range;
let new_offset = drag.start_scroll_offset + scroll_delta;
Some(new_offset.clamp(0.0, scrollable_range))
}
fn remap_drop_target(
target: &mut OptionDomNodeId,
dom_id: DomId,
node_id_map: &alloc::collections::BTreeMap<NodeId, NodeId>,
) {
let dt = match target.into_option() {
Some(dt) if dt.dom == dom_id => dt,
_ => return,
};
let old_nid = match dt.node.into_crate_internal() {
Some(nid) => nid,
None => return,
};
if let Some(&new_nid) = node_id_map.get(&old_nid) {
*target = Some(DomNodeId {
dom: dom_id,
node: crate::styled_dom::NodeHierarchyItemId::from_crate_internal(Some(new_nid)),
}).into();
} else {
*target = OptionDomNodeId::None;
}
}
pub fn remap_node_ids(
&mut self,
dom_id: DomId,
node_id_map: &alloc::collections::BTreeMap<NodeId, NodeId>,
) -> bool {
match &mut self.drag_type {
ActiveDragType::TextSelection(ref mut drag) => {
if drag.dom_id != dom_id {
return true;
}
if let Some(&new_id) = node_id_map.get(&drag.anchor_ifc_node) {
drag.anchor_ifc_node = new_id;
} else {
return false; }
if let Some(ref mut container) = drag.auto_scroll_container {
if let Some(&new_id) = node_id_map.get(container) {
*container = new_id;
} else {
drag.auto_scroll_container = None;
}
}
true
}
ActiveDragType::ScrollbarThumb(ref mut drag) => {
if let Some(&new_id) = node_id_map.get(&drag.scroll_container_node) {
drag.scroll_container_node = new_id;
true
} else {
false }
}
ActiveDragType::Node(ref mut drag) => {
if drag.dom_id != dom_id {
return true;
}
if let Some(&new_id) = node_id_map.get(&drag.node_id) {
drag.node_id = new_id;
} else {
return false; }
Self::remap_drop_target(&mut drag.current_drop_target, dom_id, node_id_map);
true
}
ActiveDragType::WindowMove(_) | ActiveDragType::WindowResize(_) => true,
ActiveDragType::FileDrop(ref mut drag) => {
Self::remap_drop_target(&mut drag.drop_target, dom_id, node_id_map);
true
}
}
}
}
azul_css::impl_option!(
DragContext,
OptionDragContext,
copy = false,
[Debug, Clone, PartialEq]
);
#[derive(Default, Debug, Copy, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct DragDelta {
pub dx: f32,
pub dy: f32,
}
impl DragDelta {
#[inline(always)]
pub const fn new(dx: f32, dy: f32) -> Self {
Self { dx, dy }
}
#[inline(always)]
pub const fn zero() -> Self {
Self::new(0.0, 0.0)
}
}
impl_option!(
DragDelta,
OptionDragDelta,
[Debug, Copy, Clone, PartialEq, PartialOrd]
);