Learning_GTK4_tree/gdk/macos/edgesnapping.c

230 lines
7.1 KiB
C
Raw Permalink Normal View History

2023-12-12 11:36:42 +01:00
/* edgesnapping.c
*
* Copyright © 2020 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include "gdkrectangle.h"
#include "edgesnapping.h"
#define LEAVE_THRESHOLD 3.0
#define ENTER_THRESHOLD 2.0
#define X1(r) ((r)->x)
#define X2(r) ((r)->x + (r)->width)
#define Y1(r) ((r)->y)
#define Y2(r) ((r)->y + (r)->height)
void
_edge_snapping_init (EdgeSnapping *self,
const GdkRectangle *geometry,
const GdkRectangle *workarea,
const GdkPoint *pointer_position,
const GdkRectangle *window)
{
g_assert (self != NULL);
g_assert (geometry != NULL);
g_assert (workarea != NULL);
g_assert (pointer_position != NULL);
self->geometry = *geometry;
self->workarea = *workarea;
self->last_pointer_position = *pointer_position;
self->pointer_offset_in_window.x = pointer_position->x - window->x;
self->pointer_offset_in_window.y = pointer_position->y - window->y;
}
static void
edge_snapping_constrain_left (EdgeSnapping *self,
int change,
const GdkRectangle *geometry,
GdkRectangle *window)
{
if (change < 0)
{
if (X1 (window) < X1 (geometry) &&
X1 (window) > X1 (geometry) - LEAVE_THRESHOLD &&
ABS (change) < LEAVE_THRESHOLD)
window->x = geometry->x;
}
/* We don't constrain when returning from left edge */
}
static void
edge_snapping_constrain_right (EdgeSnapping *self,
int change,
const GdkRectangle *geometry,
GdkRectangle *window)
{
if (change > 0)
{
if (X2 (window) > X2 (geometry) &&
X2 (window) < X2 (geometry) + LEAVE_THRESHOLD &&
ABS (change) < LEAVE_THRESHOLD)
window->x = X2 (geometry) - window->width;
}
/* We don't constrain when returning from right edge */
}
static void
edge_snapping_constrain_top (EdgeSnapping *self,
int change,
const GdkRectangle *geometry,
GdkRectangle *window)
{
if (change < 0)
{
if (Y1 (window) < Y1 (geometry))
window->y = geometry->y;
}
/* We don't constrain when returning from top edge */
}
static void
edge_snapping_constrain_bottom (EdgeSnapping *self,
int change,
const GdkRectangle *geometry,
GdkRectangle *window)
{
if (change > 0)
{
if (Y2 (window) > Y2 (geometry) &&
Y2 (window) < Y2 (geometry) + LEAVE_THRESHOLD &&
ABS (change) < LEAVE_THRESHOLD)
window->y = Y2 (geometry) - window->height;
}
else if (change < 0)
{
if (Y2 (window) < Y2 (geometry) &&
Y2 (window) > Y2 (geometry) - ENTER_THRESHOLD &&
ABS (change) < ENTER_THRESHOLD)
window->y = Y2 (geometry) - window->height;
}
}
static void
edge_snapping_constrain_horizontal (EdgeSnapping *self,
int change,
const GdkRectangle *geometry,
GdkRectangle *window)
{
g_assert (self != NULL);
g_assert (geometry != NULL);
g_assert (window != NULL);
g_assert (change != 0);
if (ABS (X1 (geometry) - X1 (window)) < ABS (X2 (geometry)) - ABS (X2 (window)))
edge_snapping_constrain_left (self, change, geometry, window);
else
edge_snapping_constrain_right (self, change, geometry, window);
}
static void
edge_snapping_constrain_vertical (EdgeSnapping *self,
int change,
const GdkRectangle *geometry,
GdkRectangle *window,
gboolean bottom_only)
{
g_assert (self != NULL);
g_assert (geometry != NULL);
g_assert (window != NULL);
g_assert (change != 0);
if (!bottom_only &&
ABS (Y1 (geometry) - Y1 (window)) < ABS (Y2 (geometry)) - ABS (Y2 (window)))
edge_snapping_constrain_top (self, change, geometry, window);
else
edge_snapping_constrain_bottom (self, change, geometry, window);
}
void
_edge_snapping_motion (EdgeSnapping *self,
const GdkPoint *pointer_position,
GdkRectangle *window)
{
GdkRectangle new_window;
GdkRectangle overlap;
GdkPoint change;
g_assert (self != NULL);
g_assert (pointer_position != NULL);
change.x = pointer_position->x - self->last_pointer_position.x;
change.y = pointer_position->y - self->last_pointer_position.y;
self->last_pointer_position = *pointer_position;
window->x += change.x;
window->y += change.y;
new_window = *window;
/* First constrain horizontal */
if (change.x)
{
edge_snapping_constrain_horizontal (self, change.x, &self->workarea, &new_window);
if (gdk_rectangle_equal (&new_window, window))
edge_snapping_constrain_horizontal (self, change.x, &self->geometry, &new_window);
}
/* Now constrain veritcally */
if (change.y)
{
edge_snapping_constrain_vertical (self, change.y, &self->workarea, &new_window, FALSE);
if (new_window.y == window->y)
edge_snapping_constrain_vertical (self, change.y, &self->geometry, &new_window, TRUE);
}
/* If the window is not placed in the monitor at all, then we need to
* just move the window onto the new screen using the original offset
* of the pointer within the window.
*/
if (!gdk_rectangle_intersect (&self->geometry, &new_window, &overlap))
{
new_window.x = pointer_position->x - self->pointer_offset_in_window.x;
new_window.y = pointer_position->y - self->pointer_offset_in_window.y;
}
/* And finally make sure we aren't underneath the top bar of the
* particular monitor.
*/
if (Y1 (&new_window) < Y1 (&self->workarea))
new_window.y = self->workarea.y;
*window = new_window;
}
void
_edge_snapping_set_monitor (EdgeSnapping *self,
const GdkRectangle *geometry,
const GdkRectangle *workarea)
{
g_assert (self != NULL);
g_assert (geometry != NULL);
g_assert (workarea != NULL);
self->geometry = *geometry;
self->workarea = *workarea;
}