Resize on border (#1347)

This commit is contained in:
Ching Pei Yang 2023-02-18 23:35:31 +01:00 committed by GitHub
parent b944386ca5
commit c92e0c05e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 228 additions and 51 deletions

View file

@ -571,22 +571,18 @@ CWindow* CCompositor::vectorToWindowTiled(const Vector2D& pos) {
return nullptr;
}
void findExtensionForVector2D(wlr_surface* surface, int x, int y, void* data) {
const auto DATA = (SExtensionFindingData*)data;
wlr_box box = {DATA->origin.x + x, DATA->origin.y + y, surface->current.width, surface->current.height};
if (wlr_box_contains_point(&box, DATA->vec.x, DATA->vec.y))
*DATA->found = surface;
}
CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
const auto PMONITOR = getMonitorFromVector(pos);
static auto* const PRESIZEONBORDER = &g_pConfigManager->getConfigValuePtr("general:resize_on_border")->intValue;
static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
static auto* const PBORDERGRABEXTEND = &g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area")->intValue;
const auto BORDER_GRAB_AREA = *PRESIZEONBORDER ? *PBORDERSIZE + *PBORDERGRABEXTEND : 0;
// special workspace
if (PMONITOR->specialWorkspaceID) {
for (auto w = m_vWindows.rbegin(); w != m_vWindows.rend(); w++) {
wlr_box box = {(*w)->m_vRealPosition.vec().x, (*w)->m_vRealPosition.vec().y, (*w)->m_vRealSize.vec().x, (*w)->m_vRealSize.vec().y};
wlr_box box = {(*w)->m_vRealPosition.vec().x - BORDER_GRAB_AREA, (*w)->m_vRealPosition.vec().y - BORDER_GRAB_AREA, (*w)->m_vRealSize.vec().x + 2 * BORDER_GRAB_AREA,
(*w)->m_vRealSize.vec().y + 2 * BORDER_GRAB_AREA};
if ((*w)->m_bIsFloating && (*w)->m_iWorkspaceID == PMONITOR->specialWorkspaceID && (*w)->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) &&
!(*w)->isHidden() && !(*w)->m_bX11ShouldntFocus)
return (*w).get();
@ -602,18 +598,14 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
// pinned windows on top of floating regardless
for (auto w = m_vWindows.rbegin(); w != m_vWindows.rend(); w++) {
wlr_box box = {(*w)->m_vRealPosition.vec().x, (*w)->m_vRealPosition.vec().y, (*w)->m_vRealSize.vec().x, (*w)->m_vRealSize.vec().y};
wlr_box box = {(*w)->m_vRealPosition.vec().x - BORDER_GRAB_AREA, (*w)->m_vRealPosition.vec().y - BORDER_GRAB_AREA, (*w)->m_vRealSize.vec().x + 2 * BORDER_GRAB_AREA,
(*w)->m_vRealSize.vec().y + 2 * BORDER_GRAB_AREA};
if ((*w)->m_bIsFloating && (*w)->m_bIsMapped && !(*w)->isHidden() && !(*w)->m_bX11ShouldntFocus && (*w)->m_bPinned) {
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y))
return w->get();
if (!(*w)->m_bIsX11) {
wlr_surface* resultSurf = nullptr;
Vector2D origin = (*w)->m_vRealPosition.vec();
SExtensionFindingData data = {origin, pos, &resultSurf};
wlr_xdg_surface_for_each_popup_surface((*w)->m_uSurface.xdg, findExtensionForVector2D, &data);
if (resultSurf)
if ((*w)->hasPopupAt(pos))
return w->get();
}
}
@ -621,7 +613,8 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
// first loop over floating cuz they're above, m_lWindows should be sorted bottom->top, for tiled it doesn't matter.
for (auto w = m_vWindows.rbegin(); w != m_vWindows.rend(); w++) {
wlr_box box = {(*w)->m_vRealPosition.vec().x, (*w)->m_vRealPosition.vec().y, (*w)->m_vRealSize.vec().x, (*w)->m_vRealSize.vec().y};
wlr_box box = {(*w)->m_vRealPosition.vec().x - BORDER_GRAB_AREA, (*w)->m_vRealPosition.vec().y - BORDER_GRAB_AREA, (*w)->m_vRealSize.vec().x + 2 * BORDER_GRAB_AREA,
(*w)->m_vRealSize.vec().y + 2 * BORDER_GRAB_AREA};
if ((*w)->m_bIsFloating && (*w)->m_bIsMapped && isWorkspaceVisible((*w)->m_iWorkspaceID) && !(*w)->isHidden() && !(*w)->m_bPinned) {
// OR windows should add focus to parent
if ((*w)->m_bX11ShouldntFocus && (*w)->m_iX11Type != 2)
@ -639,12 +632,7 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
}
if (!(*w)->m_bIsX11) {
wlr_surface* resultSurf = nullptr;
Vector2D origin = (*w)->m_vRealPosition.vec();
SExtensionFindingData data = {origin, pos, &resultSurf};
wlr_xdg_surface_for_each_popup_surface((*w)->m_uSurface.xdg, findExtensionForVector2D, &data);
if (resultSurf)
if ((*w)->hasPopupAt(pos))
return w->get();
}
}
@ -653,12 +641,7 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
// for windows, we need to check their extensions too, first.
for (auto& w : m_vWindows) {
if (!w->m_bIsX11 && !w->m_bIsFloating && w->m_bIsMapped && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->isHidden() && !w->m_bX11ShouldntFocus) {
wlr_surface* resultSurf = nullptr;
Vector2D origin = w->m_vRealPosition.vec();
SExtensionFindingData data = {origin, pos, &resultSurf};
wlr_xdg_surface_for_each_popup_surface(w->m_uSurface.xdg, findExtensionForVector2D, &data);
if (resultSurf)
if ((w)->hasPopupAt(pos))
return w.get();
}
}

View file

@ -413,3 +413,57 @@ void CWindow::updateDynamicRules() {
applyDynamicRule(r);
}
}
// check if the point is "hidden" under a rounded corner of the window
// it is assumed that the point is within the real window box (m_vRealPosition, m_vRealSize)
// otherwise behaviour is undefined
bool CWindow::isInCurvedCorner(double x, double y) {
static auto* const ROUNDING = &g_pConfigManager->getConfigValuePtr("decoration:rounding")->intValue;
static auto* const BORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
if (BORDERSIZE >= ROUNDING || ROUNDING == 0)
return false;
// (x0, y0), (x0, y1), ... are the center point of rounding at each corner
double x0 = m_vRealPosition.vec().x + *ROUNDING;
double y0 = m_vRealPosition.vec().y + *ROUNDING;
double x1 = m_vRealPosition.vec().x + m_vRealSize.vec().x - *ROUNDING;
double y1 = m_vRealPosition.vec().y + m_vRealSize.vec().y - *ROUNDING;
if (x < x0 && y < y0) {
return Vector2D{x0, y0}.distance(Vector2D{x, y}) > (double)*ROUNDING;
}
if (x > x1 && y < y0) {
return Vector2D{x1, y0}.distance(Vector2D{x, y}) > (double)*ROUNDING;
}
if (x < x0 && y > y1) {
return Vector2D{x0, y1}.distance(Vector2D{x, y}) > (double)*ROUNDING;
}
if (x > x1 && y > y1) {
return Vector2D{x1, y1}.distance(Vector2D{x, y}) > (double)*ROUNDING;
}
return false;
}
void findExtensionForVector2D(wlr_surface* surface, int x, int y, void* data) {
const auto DATA = (SExtensionFindingData*)data;
wlr_box box = {DATA->origin.x + x, DATA->origin.y + y, surface->current.width, surface->current.height};
if (wlr_box_contains_point(&box, DATA->vec.x, DATA->vec.y))
*DATA->found = surface;
}
// checks if the wayland window has a popup at pos
bool CWindow::hasPopupAt(const Vector2D& pos) {
if (m_bIsX11)
return false;
wlr_surface* resultSurf = nullptr;
Vector2D origin = m_vRealPosition.vec();
SExtensionFindingData data = {origin, pos, &resultSurf};
wlr_xdg_surface_for_each_popup_surface(m_uSurface.xdg, findExtensionForVector2D, &data);
return resultSurf;
}

View file

@ -7,6 +7,7 @@
#include "render/decorations/IHyprWindowDecoration.hpp"
#include <deque>
#include "config/ConfigDataValues.hpp"
#include "helpers/Vector2D.hpp"
enum eIdleInhibitMode
{
@ -297,7 +298,10 @@ class CWindow {
bool isHidden();
void applyDynamicRule(const SWindowRule& r);
void updateDynamicRules();
void onBorderAngleAnimEnd(void* ptr);
bool isInCurvedCorner(double x, double y);
bool hasPopupAt(const Vector2D& pos);
private:
// For hidden windows and stuff

View file

@ -46,6 +46,9 @@ void CConfigManager::setDefaultVars() {
((CGradientValueData*)configValues["general:col.inactive_border"].data.get())->reset(0xff444444);
configValues["general:cursor_inactive_timeout"].intValue = 0;
configValues["general:no_cursor_warps"].intValue = 0;
configValues["general:resize_on_border"].intValue = 0;
configValues["general:extend_border_grab_area"].intValue = 15;
configValues["general:hover_icon_on_border"].intValue = 1;
configValues["general:layout"].strValue = "dwindle";

View file

@ -30,3 +30,9 @@ Vector2D Vector2D::floor() {
Vector2D Vector2D::clamp(const Vector2D& min, const Vector2D& max) {
return Vector2D(std::clamp(this->x, min.x, max.x == 0 ? INFINITY : max.x), std::clamp(this->y, min.y, max.y == 0 ? INFINITY : max.y));
}
double Vector2D::distance(const Vector2D& other) {
double dx = x - other.x;
double dy = y - other.y;
return std::sqrt(dx * dx + dy * dy);
}

View file

@ -43,6 +43,8 @@ class Vector2D {
return Vector2D(this->x / a.x, this->y / a.y);
}
double distance(const Vector2D& other);
Vector2D clamp(const Vector2D& min, const Vector2D& max = Vector2D());
Vector2D floor();

View file

@ -195,10 +195,15 @@ void IHyprLayout::onBeginDragWindow() {
void IHyprLayout::onEndDragWindow() {
const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow;
if (!g_pCompositor->windowValidMapped(DRAGGINGWINDOW)) {
if (DRAGGINGWINDOW) {
g_pInputManager->unsetCursorImage();
if (!g_pCompositor->windowValidMapped(DRAGGINGWINDOW))
g_pInputManager->currentlyDraggedWindow = nullptr;
}
return;
}
g_pInputManager->unsetCursorImage();
g_pInputManager->currentlyDraggedWindow = nullptr;

View file

@ -294,6 +294,14 @@ bool CKeybindManager::onMouseEvent(wlr_pointer_button_event* e) {
return !found && !mouseBindWasActive;
}
void CKeybindManager::resizeWithBorder(wlr_pointer_button_event* e) {
if (e->state == WLR_BUTTON_PRESSED) {
mouse("1resizewindow");
} else {
mouse("0resizewindow");
}
}
void CKeybindManager::onSwitchEvent(const std::string& switchName) {
handleKeybinds(0, "switch:" + switchName, 0, 0, true, 0);
}

View file

@ -39,6 +39,7 @@ class CKeybindManager {
bool onKeyEvent(wlr_keyboard_key_event*, SKeyboard*);
bool onAxisEvent(wlr_pointer_axis_event*);
bool onMouseEvent(wlr_pointer_button_event*);
void resizeWithBorder(wlr_pointer_button_event*);
void onSwitchEvent(const std::string&);
void onSwitchOnEvent(const std::string&);
void onSwitchOffEvent(const std::string&);

View file

@ -51,6 +51,11 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
static auto* const PHOGFOCUS = &g_pConfigManager->getConfigValuePtr("misc:layers_hog_keyboard_focus")->intValue;
static auto* const PFLOATBEHAVIOR = &g_pConfigManager->getConfigValuePtr("input:float_switch_override_focus")->intValue;
static auto* const PMOUSEFOCUSMON = &g_pConfigManager->getConfigValuePtr("misc:mouse_move_focuses_monitor")->intValue;
static auto* const PRESIZEONBORDER = &g_pConfigManager->getConfigValuePtr("general:resize_on_border")->intValue;
static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
static auto* const PBORDERGRABEXTEND = &g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area")->intValue;
static auto* const PRESIZECURSORICON = &g_pConfigManager->getConfigValuePtr("general:hover_icon_on_border")->intValue;
const auto BORDER_GRAB_AREA = *PRESIZEONBORDER ? *PBORDERSIZE + *PBORDERGRABEXTEND : 0;
m_pFoundSurfaceToFocus = nullptr;
m_pFoundLSToFocus = nullptr;
@ -180,7 +185,8 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
// only check floating because tiled cant be over fullscreen
for (auto w = g_pCompositor->m_vWindows.rbegin(); w != g_pCompositor->m_vWindows.rend(); w++) {
wlr_box box = {(*w)->m_vRealPosition.vec().x, (*w)->m_vRealPosition.vec().y, (*w)->m_vRealSize.vec().x, (*w)->m_vRealSize.vec().y};
wlr_box box = {(*w)->m_vRealPosition.vec().x - BORDER_GRAB_AREA, (*w)->m_vRealPosition.vec().y - BORDER_GRAB_AREA, (*w)->m_vRealSize.vec().x + 2 * BORDER_GRAB_AREA,
(*w)->m_vRealSize.vec().y + 2 * BORDER_GRAB_AREA};
if ((((*w)->m_bIsFloating && (*w)->m_bIsMapped && ((*w)->m_bCreatedOverFullscreen || (*w)->m_bPinned)) ||
(g_pCompositor->isWorkspaceSpecial((*w)->m_iWorkspaceID) && PMONITOR->specialWorkspaceID)) &&
wlr_box_contains_point(&box, mouseCoords.x, mouseCoords.y) && g_pCompositor->isWorkspaceVisible((*w)->m_iWorkspaceID) && !(*w)->isHidden()) {
@ -298,6 +304,11 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
}
if (pFoundWindow) {
// change cursor icon if hovering over border, skip if mouse bind is active
if (*PRESIZEONBORDER && *PRESIZECURSORICON && !pFoundWindow->m_bIsFullscreen && !pFoundWindow->hasPopupAt(mouseCoords)) {
setCursorIconOnBorder(pFoundWindow);
}
if (*PFOLLOWMOUSE != 1 && !refocus) {
if (pFoundWindow != g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow &&
((pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR == 2) || (g_pCompositor->m_pLastWindow->m_bIsFloating != pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR != 0))) {
@ -420,10 +431,25 @@ void CInputManager::processMouseDownNormal(wlr_pointer_button_event* e) {
static auto* const PPASSMOUSE = &g_pConfigManager->getConfigValuePtr("binds:pass_mouse_when_bound")->intValue;
const auto PASS = g_pKeybindManager->onMouseEvent(e);
static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue;
static auto* const PRESIZEONBORDER = &g_pConfigManager->getConfigValuePtr("general:resize_on_border")->intValue;
if (!PASS && !*PPASSMOUSE)
return;
// clicking on border triggers resize
// TODO detect click on LS properly
if (*PRESIZEONBORDER && !m_bLastFocusOnLS) {
const auto mouseCoords = g_pInputManager->getMouseCoordsInternal();
const auto w = g_pCompositor->vectorToWindowIdeal(mouseCoords);
if (w && !w->m_bIsFullscreen) {
const wlr_box real = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if ((!wlr_box_contains_point(&real, mouseCoords.x, mouseCoords.y) || w->isInCurvedCorner(mouseCoords.x, mouseCoords.y)) && !w->hasPopupAt(mouseCoords)) {
g_pKeybindManager->resizeWithBorder(e);
return;
}
}
}
switch (e->state) {
case WLR_BUTTON_PRESSED:
if (*PFOLLOWMOUSE == 3) // don't refocus on full loose
@ -1340,3 +1366,74 @@ void CInputManager::releaseAllMouseButtons() {
m_lCurrentlyHeldButtons.clear();
}
void CInputManager::setCursorIconOnBorder(CWindow* w) {
// do not override cursor icons set by mouse binds
if (g_pKeybindManager->m_bIsMouseBindActive) {
m_eBorderIconDirection = BORDERICON_NONE;
return;
}
static auto* const PROUNDING = &g_pConfigManager->getConfigValuePtr("decoration:rounding")->intValue;
static const auto* PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
// give a small leeway (10 px) for corner icon
const auto CORNER = *PROUNDING + *PBORDERSIZE + 10;
const auto mouseCoords = getMouseCoordsInternal();
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
eBorderIconDirection direction = BORDERICON_NONE;
if (wlr_box_contains_point(&box, mouseCoords.x, mouseCoords.y)) {
if (!w->isInCurvedCorner(mouseCoords.x, mouseCoords.y)) {
direction = BORDERICON_NONE;
} else {
if (mouseCoords.y < box.y + CORNER) {
if (mouseCoords.x < box.x + CORNER)
direction = BORDERICON_UP_LEFT;
else
direction = BORDERICON_UP_RIGHT;
} else {
if (mouseCoords.x < box.x + CORNER)
direction = BORDERICON_DOWN_LEFT;
else
direction = BORDERICON_DOWN_RIGHT;
}
}
} else {
if (mouseCoords.y < box.y + CORNER) {
if (mouseCoords.x < box.x + CORNER)
direction = BORDERICON_UP_LEFT;
else if (mouseCoords.x > box.x + box.width - CORNER)
direction = BORDERICON_UP_RIGHT;
else
direction = BORDERICON_UP;
} else if (mouseCoords.y > box.y + box.height - CORNER) {
if (mouseCoords.x < box.x + CORNER)
direction = BORDERICON_DOWN_LEFT;
else if (mouseCoords.x > box.x + box.width - CORNER)
direction = BORDERICON_DOWN_RIGHT;
else
direction = BORDERICON_DOWN;
} else {
if (mouseCoords.x < box.x + CORNER)
direction = BORDERICON_LEFT;
else if (mouseCoords.x > box.x + box.width - CORNER)
direction = BORDERICON_RIGHT;
}
}
if (direction == m_eBorderIconDirection)
return;
m_eBorderIconDirection = direction;
switch (direction) {
case BORDERICON_NONE: unsetCursorImage(); break;
case BORDERICON_UP: setCursorImageUntilUnset("top_side"); break;
case BORDERICON_DOWN: setCursorImageUntilUnset("bottom_side"); break;
case BORDERICON_LEFT: setCursorImageUntilUnset("left_side"); break;
case BORDERICON_RIGHT: setCursorImageUntilUnset("right_side"); break;
case BORDERICON_UP_LEFT: setCursorImageUntilUnset("top_left_corner"); break;
case BORDERICON_DOWN_LEFT: setCursorImageUntilUnset("bottom_left_corner"); break;
case BORDERICON_UP_RIGHT: setCursorImageUntilUnset("top_right_corner"); break;
case BORDERICON_DOWN_RIGHT: setCursorImageUntilUnset("bottom_right_corner"); break;
}
}

View file

@ -7,19 +7,29 @@
#include "../../helpers/Timer.hpp"
#include "InputMethodRelay.hpp"
enum eClickBehaviorMode
{
enum eClickBehaviorMode {
CLICKMODE_DEFAULT = 0,
CLICKMODE_KILL
};
enum eMouseBindMode
{
enum eMouseBindMode {
MBIND_INVALID = -1,
MBIND_MOVE = 0,
MBIND_RESIZE
};
enum eBorderIconDirection {
BORDERICON_NONE,
BORDERICON_UP,
BORDERICON_DOWN,
BORDERICON_LEFT,
BORDERICON_RIGHT,
BORDERICON_UP_LEFT,
BORDERICON_DOWN_LEFT,
BORDERICON_UP_RIGHT,
BORDERICON_DOWN_RIGHT,
};
struct STouchData {
CWindow* touchFocusWindow = nullptr;
SLayerSurface* touchFocusLS = nullptr;
@ -166,6 +176,7 @@ class CInputManager {
private:
bool m_bCursorImageOverriden = false;
eBorderIconDirection m_eBorderIconDirection = BORDERICON_NONE;
// for click behavior override
eClickBehaviorMode m_ecbClickBehavior = CLICKMODE_DEFAULT;
@ -196,6 +207,9 @@ class CInputManager {
// swipe
void beginWorkspaceSwipe();
void setBorderCursorIcon(eBorderIconDirection);
void setCursorIconOnBorder(CWindow* w);
friend class CKeybindManager;
};