1pub use crate::drag::{DragDelta, OptionDragDelta};
9
10#[derive(Copy, Default, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
12#[repr(C)]
13pub struct LogicalRect {
14 pub origin: LogicalPosition,
15 pub size: LogicalSize,
16}
17
18impl core::fmt::Debug for LogicalRect {
19 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
20 write!(f, "{} @ {}", self.size, self.origin)
21 }
22}
23
24impl core::fmt::Display for LogicalRect {
25 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
26 write!(f, "{} @ {}", self.size, self.origin)
27 }
28}
29
30impl LogicalRect {
31 #[must_use]
32 pub const fn zero() -> Self {
33 Self::new(LogicalPosition::zero(), LogicalSize::zero())
34 }
35 #[must_use]
36 pub const fn new(origin: LogicalPosition, size: LogicalSize) -> Self {
37 Self { origin, size }
38 }
39
40 #[inline]
42 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
43 self.origin.x *= scale_factor;
44 self.origin.y *= scale_factor;
45 self.size.width *= scale_factor;
46 self.size.height *= scale_factor;
47 }
48
49 #[inline]
51 #[must_use]
52 pub fn max_x(&self) -> f32 {
53 self.origin.x + self.size.width
54 }
55 #[inline]
57 #[must_use]
58 pub const fn min_x(&self) -> f32 {
59 self.origin.x
60 }
61 #[inline]
63 #[must_use]
64 pub fn max_y(&self) -> f32 {
65 self.origin.y + self.size.height
66 }
67 #[inline]
69 #[must_use]
70 pub const fn min_y(&self) -> f32 {
71 self.origin.y
72 }
73
74 #[inline]
76 #[must_use]
77 pub fn intersects(&self, other: Self) -> bool {
78 if self.max_x() <= other.min_x() || other.max_x() <= self.min_x() {
80 return false;
81 }
82
83 if self.max_y() <= other.min_y() || other.max_y() <= self.min_y() {
85 return false;
86 }
87
88 true
90 }
91
92 #[inline]
94 #[must_use]
95 pub fn contains(&self, point: LogicalPosition) -> bool {
96 point.x >= self.min_x()
97 && point.x < self.max_x()
98 && point.y >= self.min_y()
99 && point.y < self.max_y()
100 }
101
102 #[inline]
106 #[must_use]
107 pub fn hit_test(&self, other: &LogicalPosition) -> Option<LogicalPosition> {
108 let dx_left_edge = other.x - self.min_x();
109 let dx_right_edge = self.max_x() - other.x;
110 let dy_top_edge = other.y - self.min_y();
111 let dy_bottom_edge = self.max_y() - other.y;
112 if dx_left_edge >= 0.0 && dx_right_edge > 0.0 && dy_top_edge >= 0.0 && dy_bottom_edge > 0.0
118 {
119 Some(LogicalPosition::new(dx_left_edge, dy_top_edge))
120 } else {
121 None
122 }
123 }
124}
125
126impl_vec!(
127 LogicalRect,
128 LogicalRectVec,
129 LogicalRectVecDestructor,
130 LogicalRectVecDestructorType,
131 LogicalRectVecSlice,
132 OptionLogicalRect
133);
134impl_vec_clone!(LogicalRect, LogicalRectVec, LogicalRectVecDestructor);
135impl_vec_debug!(LogicalRect, LogicalRectVec);
136impl_vec_partialeq!(LogicalRect, LogicalRectVec);
137impl_vec_partialord!(LogicalRect, LogicalRectVec);
138impl_vec_ord!(LogicalRect, LogicalRectVec);
139impl_vec_hash!(LogicalRect, LogicalRectVec);
140impl_vec_eq!(LogicalRect, LogicalRectVec);
141
142use core::{
143 cmp::Ordering,
144 hash::{Hash, Hasher},
145 ops::{self, AddAssign, SubAssign},
146};
147
148use azul_css::props::layout::LayoutWritingMode;
149
150#[derive(Default, Copy, Clone)]
156#[repr(C)]
157pub struct LogicalPosition {
158 pub x: f32,
159 pub y: f32,
160}
161
162impl PartialEq for LogicalPosition {
163 fn eq(&self, other: &Self) -> bool {
164 quantize(self.x) == quantize(other.x) && quantize(self.y) == quantize(other.y)
165 }
166}
167
168impl LogicalPosition {
169 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
171 self.x *= scale_factor;
172 self.y *= scale_factor;
173 }
174}
175
176impl SubAssign<Self> for LogicalPosition {
177 fn sub_assign(&mut self, other: Self) {
178 self.x -= other.x;
179 self.y -= other.y;
180 }
181}
182
183impl AddAssign<Self> for LogicalPosition {
184 fn add_assign(&mut self, other: Self) {
185 self.x += other.x;
186 self.y += other.y;
187 }
188}
189
190impl core::fmt::Debug for LogicalPosition {
191 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
192 write!(f, "({}, {})", self.x, self.y)
193 }
194}
195
196impl core::fmt::Display for LogicalPosition {
197 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
198 write!(f, "({}, {})", self.x, self.y)
199 }
200}
201
202impl ops::Add for LogicalPosition {
203 type Output = Self;
204
205 #[inline]
206 fn add(self, other: Self) -> Self {
207 Self {
208 x: self.x + other.x,
209 y: self.y + other.y,
210 }
211 }
212}
213
214impl ops::Sub for LogicalPosition {
215 type Output = Self;
216
217 #[inline]
218 fn sub(self, other: Self) -> Self {
219 Self {
220 x: self.x - other.x,
221 y: self.y - other.y,
222 }
223 }
224}
225
226const DECIMAL_MULTIPLIER: f32 = 1000.0;
229
230#[allow(clippy::cast_possible_truncation)]
234fn quantize(value: f32) -> i64 {
235 if value.is_nan() {
240 return i64::MIN;
241 }
242 (value * DECIMAL_MULTIPLIER) as i64
247}
248
249impl_option!(
250 LogicalPosition,
251 OptionLogicalPosition,
252 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd]
253);
254
255impl PartialOrd for LogicalPosition {
258 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
259 Some(self.cmp(other))
260 }
261}
262impl Ord for LogicalPosition {
263 fn cmp(&self, other: &Self) -> Ordering {
264 let self_x = quantize(self.x);
265 let self_y = quantize(self.y);
266 let other_x = quantize(other.x);
267 let other_y = quantize(other.y);
268 self_x.cmp(&other_x).then(self_y.cmp(&other_y))
269 }
270}
271
272impl Eq for LogicalPosition {}
273
274impl Hash for LogicalPosition {
275 fn hash<H>(&self, state: &mut H)
276 where
277 H: Hasher,
278 {
279 let self_x = quantize(self.x);
280 let self_y = quantize(self.y);
281 self_x.hash(state);
282 self_y.hash(state);
283 }
284}
285
286impl LogicalPosition {
287 #[must_use]
289 pub const fn main(&self, wm: LayoutWritingMode) -> f32 {
290 match wm {
291 LayoutWritingMode::HorizontalTb => self.y,
292 LayoutWritingMode::VerticalRl | LayoutWritingMode::VerticalLr => self.x,
293 }
294 }
295
296 #[must_use]
298 pub const fn cross(&self, wm: LayoutWritingMode) -> f32 {
299 match wm {
300 LayoutWritingMode::HorizontalTb => self.x,
301 LayoutWritingMode::VerticalRl | LayoutWritingMode::VerticalLr => self.y,
302 }
303 }
304
305 #[must_use]
307 pub const fn from_main_cross(main: f32, cross: f32, wm: LayoutWritingMode) -> Self {
308 match wm {
309 LayoutWritingMode::HorizontalTb => Self::new(cross, main),
310 LayoutWritingMode::VerticalRl | LayoutWritingMode::VerticalLr => Self::new(main, cross),
311 }
312 }
313}
314
315#[derive(Default, Copy, Clone)]
319#[repr(C)]
320pub struct LogicalSize {
321 pub width: f32,
322 pub height: f32,
323}
324
325impl PartialEq for LogicalSize {
326 fn eq(&self, other: &Self) -> bool {
327 quantize(self.width) == quantize(other.width)
328 && quantize(self.height) == quantize(other.height)
329 }
330}
331
332impl LogicalSize {
333 #[allow(clippy::return_self_not_must_use)]
337 pub fn scale_for_dpi(&mut self, scale_factor: f32) -> Self {
338 self.width *= scale_factor;
339 self.height *= scale_factor;
340 *self
341 }
342
343 #[must_use]
345 pub const fn from_main_cross(main: f32, cross: f32, wm: LayoutWritingMode) -> Self {
346 match wm {
347 LayoutWritingMode::HorizontalTb => Self::new(cross, main),
348 LayoutWritingMode::VerticalRl | LayoutWritingMode::VerticalLr => Self::new(main, cross),
349 }
350 }
351}
352
353impl core::fmt::Debug for LogicalSize {
354 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
355 write!(f, "{}x{}", self.width, self.height)
356 }
357}
358
359impl core::fmt::Display for LogicalSize {
360 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
361 write!(f, "{}x{}", self.width, self.height)
362 }
363}
364
365impl_option!(
366 LogicalSize,
367 OptionLogicalSize,
368 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
371);
372
373impl_option!(
374 LogicalRect,
375 OptionLogicalRect,
376 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd]
377);
378
379impl PartialOrd for LogicalSize {
382 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
383 Some(self.cmp(other))
384 }
385}
386impl Ord for LogicalSize {
387 fn cmp(&self, other: &Self) -> Ordering {
388 let self_width = quantize(self.width);
389 let self_height = quantize(self.height);
390 let other_width = quantize(other.width);
391 let other_height = quantize(other.height);
392 self_width
393 .cmp(&other_width)
394 .then(self_height.cmp(&other_height))
395 }
396}
397
398impl Eq for LogicalSize {}
399
400impl Hash for LogicalSize {
401 fn hash<H>(&self, state: &mut H)
402 where
403 H: Hasher,
404 {
405 let self_width = quantize(self.width);
406 let self_height = quantize(self.height);
407 self_width.hash(state);
408 self_height.hash(state);
409 }
410}
411
412impl LogicalSize {
413 #[must_use]
415 pub const fn main(&self, wm: LayoutWritingMode) -> f32 {
416 match wm {
417 LayoutWritingMode::HorizontalTb => self.height,
418 LayoutWritingMode::VerticalRl | LayoutWritingMode::VerticalLr => self.width,
419 }
420 }
421
422 #[must_use]
424 pub const fn cross(&self, wm: LayoutWritingMode) -> f32 {
425 match wm {
426 LayoutWritingMode::HorizontalTb => self.width,
427 LayoutWritingMode::VerticalRl | LayoutWritingMode::VerticalLr => self.height,
428 }
429 }
430
431 #[must_use]
433 pub const fn with_main(self, wm: LayoutWritingMode, value: f32) -> Self {
434 match wm {
435 LayoutWritingMode::HorizontalTb => Self {
436 height: value,
437 ..self
438 },
439 LayoutWritingMode::VerticalRl | LayoutWritingMode::VerticalLr => Self {
440 width: value,
441 ..self
442 },
443 }
444 }
445
446 #[must_use]
448 pub const fn with_cross(self, wm: LayoutWritingMode, value: f32) -> Self {
449 match wm {
450 LayoutWritingMode::HorizontalTb => Self {
451 width: value,
452 ..self
453 },
454 LayoutWritingMode::VerticalRl | LayoutWritingMode::VerticalLr => Self {
455 height: value,
456 ..self
457 },
458 }
459 }
460}
461
462#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
464#[repr(C)]
465pub struct PhysicalPosition<T> {
466 pub x: T,
467 pub y: T,
468}
469
470impl<T: ::core::fmt::Display> ::core::fmt::Debug for PhysicalPosition<T> {
471 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
472 write!(f, "({}, {})", self.x, self.y)
473 }
474}
475
476pub type PhysicalPositionI32 = PhysicalPosition<i32>;
477impl_option!(
478 PhysicalPositionI32,
479 OptionPhysicalPositionI32,
480 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd]
481);
482
483#[derive(Ord, Hash, Eq, Copy, Clone, PartialEq, PartialOrd)]
485#[repr(C)]
486pub struct PhysicalSize<T> {
487 pub width: T,
488 pub height: T,
489}
490
491impl<T: ::core::fmt::Display> ::core::fmt::Debug for PhysicalSize<T> {
492 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
493 write!(f, "{}x{}", self.width, self.height)
494 }
495}
496
497pub type PhysicalSizeU32 = PhysicalSize<u32>;
498impl_option!(
499 PhysicalSizeU32,
500 OptionPhysicalSizeU32,
501 [Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash]
502);
503pub type PhysicalSizeF32 = PhysicalSize<f32>;
504impl_option!(
505 PhysicalSizeF32,
506 OptionPhysicalSizeF32,
507 [Debug, Copy, Clone, PartialEq, PartialOrd]
508);
509
510impl LogicalPosition {
511 #[inline]
512 #[must_use]
513 pub const fn new(x: f32, y: f32) -> Self {
514 Self { x, y }
515 }
516 #[inline]
517 #[must_use]
518 pub const fn zero() -> Self {
519 Self::new(0.0, 0.0)
520 }
521 #[inline]
523 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
524 #[must_use]
525 pub fn to_physical(self, hidpi_factor: f32) -> PhysicalPosition<u32> {
526 PhysicalPosition {
527 x: libm::roundf(self.x * hidpi_factor) as u32,
528 y: libm::roundf(self.y * hidpi_factor) as u32,
529 }
530 }
531}
532
533impl<T> PhysicalPosition<T> {
534 #[inline]
535 pub const fn new(x: T, y: T) -> Self {
536 Self { x, y }
537 }
538}
539
540impl PhysicalPosition<i32> {
541 #[inline]
542 #[must_use]
543 pub const fn zero() -> Self {
544 Self::new(0, 0)
545 }
546 #[inline]
548 #[allow(clippy::cast_precision_loss)]
549 #[must_use]
550 pub fn to_logical(self, hidpi_factor: f32) -> LogicalPosition {
551 LogicalPosition {
552 x: self.x as f32 / hidpi_factor,
553 y: self.y as f32 / hidpi_factor,
554 }
555 }
556}
557
558impl PhysicalPosition<f64> {
559 #[inline]
560 #[must_use]
561 pub const fn zero() -> Self {
562 Self::new(0.0, 0.0)
563 }
564 #[inline]
566 #[allow(clippy::cast_possible_truncation)]
567 #[must_use]
568 pub fn to_logical(self, hidpi_factor: f32) -> LogicalPosition {
569 LogicalPosition {
570 x: self.x as f32 / hidpi_factor,
571 y: self.y as f32 / hidpi_factor,
572 }
573 }
574}
575
576impl LogicalSize {
577 #[inline]
578 #[must_use]
579 pub const fn new(width: f32, height: f32) -> Self {
580 Self { width, height }
581 }
582 #[inline]
583 #[must_use]
584 pub const fn zero() -> Self {
585 Self::new(0.0, 0.0)
586 }
587 #[inline]
589 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
590 #[must_use]
591 pub fn to_physical(self, hidpi_factor: f32) -> PhysicalSize<u32> {
592 PhysicalSize {
593 width: libm::roundf(self.width * hidpi_factor) as u32,
594 height: libm::roundf(self.height * hidpi_factor) as u32,
595 }
596 }
597}
598
599impl<T> PhysicalSize<T> {
600 #[inline]
601 pub const fn new(width: T, height: T) -> Self {
602 Self { width, height }
603 }
604}
605
606impl PhysicalSize<u32> {
607 #[inline]
608 #[must_use]
609 pub const fn zero() -> Self {
610 Self::new(0, 0)
611 }
612 #[inline]
614 #[allow(clippy::cast_precision_loss)]
615 #[must_use]
616 pub fn to_logical(self, hidpi_factor: f32) -> LogicalSize {
617 LogicalSize {
618 width: self.width as f32 / hidpi_factor,
619 height: self.height as f32 / hidpi_factor,
620 }
621 }
622}
623
624#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
634#[repr(C)]
635pub enum CoordinateSpace {
636 Window,
639
640 ScrollFrame,
643
644 Parent,
646
647 ReferenceFrame,
649}
650
651#[derive(Default, Debug, Copy, Clone, PartialEq, PartialOrd)]
658#[repr(C)]
659pub struct ScreenPosition {
660 pub x: f32,
661 pub y: f32,
662}
663
664impl ScreenPosition {
665 #[inline]
666 #[must_use]
667 pub const fn new(x: f32, y: f32) -> Self {
668 Self { x, y }
669 }
670 #[inline]
671 #[must_use]
672 pub const fn zero() -> Self {
673 Self::new(0.0, 0.0)
674 }
675 #[inline]
677 #[must_use]
678 pub const fn to_logical(self) -> LogicalPosition {
679 LogicalPosition {
680 x: self.x,
681 y: self.y,
682 }
683 }
684 #[inline]
686 #[must_use]
687 pub const fn from_logical(p: LogicalPosition) -> Self {
688 Self { x: p.x, y: p.y }
689 }
690}
691
692impl_option!(
693 ScreenPosition,
694 OptionScreenPosition,
695 [Debug, Copy, Clone, PartialEq, PartialOrd]
696);
697
698#[derive(Default, Debug, Copy, Clone, PartialEq, PartialOrd)]
700#[repr(C)]
701pub struct CursorNodePosition {
702 pub x: f32,
703 pub y: f32,
704}
705
706impl CursorNodePosition {
707 #[inline]
708 #[must_use]
709 pub const fn new(x: f32, y: f32) -> Self {
710 Self { x, y }
711 }
712 #[inline]
713 #[must_use]
714 pub const fn zero() -> Self {
715 Self::new(0.0, 0.0)
716 }
717 #[inline]
718 #[must_use]
719 pub const fn to_logical(self) -> LogicalPosition {
720 LogicalPosition {
721 x: self.x,
722 y: self.y,
723 }
724 }
725 #[inline]
726 #[must_use]
727 pub const fn from_logical(p: LogicalPosition) -> Self {
728 Self { x: p.x, y: p.y }
729 }
730}
731
732impl_option!(
733 CursorNodePosition,
734 OptionCursorNodePosition,
735 [Debug, Copy, Clone, PartialEq, PartialOrd]
736);
737
738#[cfg(test)]
739#[path = "geom_test.rs"]
740mod geom_test;