mirror of
https://github.com/hyprwm/Hyprland
synced 2026-05-07 10:18:07 +02:00
Merge a5e918648f into 6dc42e0f6d
This commit is contained in:
commit
3ad4b1c767
8 changed files with 335 additions and 7 deletions
|
|
@ -57,6 +57,7 @@
|
|||
#include "../../managers/input/trackpad/gestures/FloatGesture.hpp"
|
||||
#include "../../managers/input/trackpad/gestures/FullscreenGesture.hpp"
|
||||
#include "../../managers/input/trackpad/gestures/CursorZoomGesture.hpp"
|
||||
#include "../../managers/input/trackpad/gestures/ScrollMoveGesture.hpp"
|
||||
|
||||
#include "../../event/EventBus.hpp"
|
||||
|
||||
|
|
@ -1979,7 +1980,9 @@ std::optional<std::string> CConfigManager::handleGesture(const std::string& comm
|
|||
else if (data[startDataIdx] == "cursorZoom") {
|
||||
result = g_pTrackpadGestures->addGesture(makeUnique<CCursorZoomTrackpadGesture>(std::string{data[startDataIdx + 1]}, std::string{data[startDataIdx + 2]}), fingerCount,
|
||||
direction, modMask, deltaScale, disableInhibit);
|
||||
} else if (data[startDataIdx] == "unset")
|
||||
} else if (data[startDataIdx] == "scrollMove")
|
||||
result = g_pTrackpadGestures->addGesture(makeUnique<CScrollMoveTrackpadGesture>(), fingerCount, direction, modMask, deltaScale, disableInhibit);
|
||||
else if (data[startDataIdx] == "unset")
|
||||
result = g_pTrackpadGestures->removeGesture(fingerCount, direction, modMask, deltaScale, disableInhibit);
|
||||
else
|
||||
return std::format("Invalid gesture: {}", data[startDataIdx]);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include "../../../managers/input/trackpad/gestures/ResizeGesture.hpp"
|
||||
#include "../../../managers/input/trackpad/gestures/SpecialWorkspaceGesture.hpp"
|
||||
#include "../../../managers/input/trackpad/gestures/WorkspaceSwipeGesture.hpp"
|
||||
#include "../../../managers/input/trackpad/gestures/ScrollMoveGesture.hpp"
|
||||
#include "../../../managers/permissions/DynamicPermissionManager.hpp"
|
||||
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
|
|
@ -721,7 +722,7 @@ static int hlWorkspaceRule(lua_State* L) {
|
|||
|
||||
static int hlGesture(lua_State* L) {
|
||||
if (!lua_istable(L, 1))
|
||||
return Internal::configError(L, "hl.gesture: expected a table, e.g. { fingers = 3, direction = \"horizontal\", action = \"workspace\" }");
|
||||
return Internal::configError(L, R"(hl.gesture: expected a table, e.g. { fingers = 3, direction = "horizontal", action = "workspace" })");
|
||||
|
||||
CLuaConfigInt fingersParser(0, 2, 9);
|
||||
auto fingersErr = Internal::parseTableField(L, 1, "fingers", fingersParser);
|
||||
|
|
@ -842,8 +843,10 @@ static int hlGesture(lua_State* L) {
|
|||
result = g_pTrackpadGestures->addGesture(makeUnique<CFloatTrackpadGesture>(mode), fingerCount, direction, modMask, deltaScale, disableInhibit);
|
||||
else if (action == "fullscreen")
|
||||
result = g_pTrackpadGestures->addGesture(makeUnique<CFullscreenTrackpadGesture>(mode), fingerCount, direction, modMask, deltaScale, disableInhibit);
|
||||
else if (action == "cursorZoom")
|
||||
else if (action == "cursor_zoom" || action == "cursorZoom")
|
||||
result = g_pTrackpadGestures->addGesture(makeUnique<CCursorZoomTrackpadGesture>(zoomLevel, mode), fingerCount, direction, modMask, deltaScale, disableInhibit);
|
||||
else if (action == "scroll_move")
|
||||
result = g_pTrackpadGestures->addGesture(makeUnique<CScrollMoveTrackpadGesture>(), fingerCount, direction, modMask, deltaScale, disableInhibit);
|
||||
else if (action == "unset")
|
||||
result = g_pTrackpadGestures->removeGesture(fingerCount, direction, modMask, deltaScale, disableInhibit);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ std::vector<SP<IValue>> Values::getConfigValues() {
|
|||
MS<Bool>("gestures:workspace_swipe_forever", "if enabled, swiping will not clamp at the neighboring workspaces but continue to the further ones.", false),
|
||||
MS<Bool>("gestures:workspace_swipe_use_r", "if enabled, swiping will use the r prefix instead of the m prefix for finding workspaces.", false),
|
||||
MS<Int>("gestures:close_max_timeout", "Timeout for closing windows with the close gesture, in ms.", 1000, {.min = 10, .max = 2000}),
|
||||
|
||||
MS<Bool>("gestures:scrolling:move_snap_to_grid", "When releasing the scroll move gesture, whether it shoud try to snap to the grid.", true),
|
||||
/*
|
||||
* group:
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -274,14 +274,14 @@ size_t CScrollTapeController::getStripAtCenter(const CBox& usableArea, bool full
|
|||
if (m_strips.empty())
|
||||
return 0;
|
||||
|
||||
const double usablePrimary = getPrimary(usableArea.size());
|
||||
double currentPos = m_offset;
|
||||
const double viewportCenter = m_offset + getPrimary(usableArea.size()) / 2.0;
|
||||
double currentPos = 0.0;
|
||||
|
||||
for (size_t i = 0; i < m_strips.size(); ++i) {
|
||||
const double stripSize = calculateStripSize(i, usableArea, fullscreenOnOne);
|
||||
currentPos += stripSize;
|
||||
|
||||
if (currentPos >= usablePrimary / 2.0 - 2.0)
|
||||
if (currentPos >= viewportCenter - 2.0)
|
||||
return i;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1497,6 +1497,158 @@ Config::ErrorResult CScrollingAlgorithm::layoutMsg(const std::string_view& sv) {
|
|||
return {};
|
||||
}
|
||||
|
||||
void CScrollingAlgorithm::moveTape(float delta) {
|
||||
if (delta == 0.F)
|
||||
return;
|
||||
|
||||
m_scrollingData->controller->adjustOffset(-delta);
|
||||
m_scrollingData->recalculate();
|
||||
}
|
||||
|
||||
void CScrollingAlgorithm::moveTapeNormalized(double delta) {
|
||||
const double primary = primaryViewportSize();
|
||||
if (primary <= 0.0 || delta == 0.0)
|
||||
return;
|
||||
|
||||
moveTape(delta * primary);
|
||||
}
|
||||
|
||||
void CScrollingAlgorithm::snapToGrid() {
|
||||
snapToProjectedOffset(normalizedTapeOffset());
|
||||
}
|
||||
|
||||
SP<SColumnData> CScrollingAlgorithm::snapToProjectedOffset(double projectedNormalizedOffset) {
|
||||
static const auto PFSONONE = CConfigValue<Config::INTEGER>("scrolling:fullscreen_on_one_column");
|
||||
static const auto PFITMETHOD = CConfigValue<Config::INTEGER>("scrolling:focus_fit_method");
|
||||
|
||||
const auto USABLE = usableArea();
|
||||
auto& controller = *m_scrollingData->controller;
|
||||
|
||||
if (controller.stripCount() == 0)
|
||||
return nullptr;
|
||||
|
||||
const double usablePrimary = controller.isPrimaryHorizontal() ? USABLE.w : USABLE.h;
|
||||
if (usablePrimary <= 0.0)
|
||||
return nullptr;
|
||||
|
||||
const double maxExtent = controller.calculateMaxExtent(USABLE, *PFSONONE);
|
||||
if (maxExtent <= 0.0)
|
||||
return nullptr;
|
||||
|
||||
const double projectedOffset = projectedNormalizedOffset * usablePrimary;
|
||||
|
||||
double bestOffset = 0.0;
|
||||
double bestDelta = 0.0;
|
||||
double bestCenterDelta = 0.0;
|
||||
size_t bestIndex = 0;
|
||||
bool foundSnap = false;
|
||||
|
||||
auto centerOffsetFor = [&](size_t index) {
|
||||
const double start = controller.calculateStripStart(index, USABLE, *PFSONONE);
|
||||
const double size = controller.calculateStripSize(index, USABLE, *PFSONONE);
|
||||
|
||||
return start - (usablePrimary - size) / 2.0;
|
||||
};
|
||||
|
||||
auto fitOffsetFor = [&](size_t index) {
|
||||
const double start = controller.calculateStripStart(index, USABLE, *PFSONONE);
|
||||
const double size = controller.calculateStripSize(index, USABLE, *PFSONONE);
|
||||
const double lo = start - usablePrimary + size;
|
||||
const double hi = start;
|
||||
|
||||
if (lo > hi)
|
||||
return centerOffsetFor(index);
|
||||
|
||||
return std::clamp(projectedOffset, lo, hi);
|
||||
};
|
||||
|
||||
auto considerColumn = [&](size_t index) {
|
||||
const double offset = *PFITMETHOD == 1 ? fitOffsetFor(index) : centerOffsetFor(index);
|
||||
const double delta = std::abs(offset - projectedOffset);
|
||||
const double start = controller.calculateStripStart(index, USABLE, *PFSONONE);
|
||||
const double size = controller.calculateStripSize(index, USABLE, *PFSONONE);
|
||||
const double centerDelta = std::abs((start + size / 2.0) - (projectedOffset + usablePrimary / 2.0));
|
||||
const bool betterFit = delta < bestDelta;
|
||||
const bool betterTieBreak = delta == bestDelta && centerDelta < bestCenterDelta;
|
||||
|
||||
if (!foundSnap || betterFit || betterTieBreak) {
|
||||
bestOffset = offset;
|
||||
bestDelta = delta;
|
||||
bestCenterDelta = centerDelta;
|
||||
bestIndex = index;
|
||||
foundSnap = true;
|
||||
}
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < controller.stripCount(); ++i)
|
||||
considerColumn(i);
|
||||
|
||||
if (!foundSnap)
|
||||
return nullptr;
|
||||
|
||||
controller.setOffset(bestOffset);
|
||||
m_scrollingData->recalculate();
|
||||
|
||||
if (bestIndex < m_scrollingData->columns.size())
|
||||
return m_scrollingData->columns[bestIndex];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CScrollingAlgorithm::focusColumn(SP<SColumnData> column) {
|
||||
if (!column || column->targetDatas.empty()) {
|
||||
focusTargetUpdate(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
auto targetData = column->lastFocusedTarget.lock();
|
||||
|
||||
if (!targetData || targetData->column.lock() != column || !targetData->target || !Desktop::View::validMapped(targetData->target->window())) {
|
||||
targetData = nullptr;
|
||||
|
||||
for (const auto& candidate : column->targetDatas) {
|
||||
if (candidate->target && Desktop::View::validMapped(candidate->target->window())) {
|
||||
targetData = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
focusTargetUpdate(targetData ? targetData->target.lock() : nullptr);
|
||||
}
|
||||
|
||||
SP<SColumnData> CScrollingAlgorithm::getColumnAtViewportCenter() {
|
||||
return m_scrollingData ? m_scrollingData->atCenter() : nullptr;
|
||||
}
|
||||
|
||||
SP<SColumnData> CScrollingAlgorithm::currentColumn() {
|
||||
auto focus = Desktop::focusState()->window();
|
||||
|
||||
if (!focus)
|
||||
return nullptr;
|
||||
|
||||
auto data = dataFor(focus->layoutTarget());
|
||||
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
return data->column.lock();
|
||||
}
|
||||
|
||||
double CScrollingAlgorithm::primaryViewportSize() {
|
||||
const auto USABLE = usableArea();
|
||||
|
||||
return m_scrollingData->controller->isPrimaryHorizontal() ? USABLE.w : USABLE.h;
|
||||
}
|
||||
|
||||
double CScrollingAlgorithm::normalizedTapeOffset() {
|
||||
const double primary = primaryViewportSize();
|
||||
if (primary <= 0.0)
|
||||
return 0.0;
|
||||
|
||||
return m_scrollingData->controller->getOffset() / primary;
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CScrollingAlgorithm::predictSizeForNewTarget() {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,6 +109,17 @@ namespace Layout::Tiled {
|
|||
virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
|
||||
virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
|
||||
void moveTape(float delta);
|
||||
void moveTapeNormalized(double delta);
|
||||
void snapToGrid();
|
||||
SP<SColumnData> snapToProjectedOffset(double projectedNormalizedOffset);
|
||||
void focusColumn(SP<SColumnData> column);
|
||||
SP<SColumnData> getColumnAtViewportCenter();
|
||||
SP<SColumnData> currentColumn();
|
||||
|
||||
double primaryViewportSize();
|
||||
double normalizedTapeOffset();
|
||||
|
||||
CBox usableArea();
|
||||
SP<SScrollingTargetData> dataFor(SP<ITarget> t);
|
||||
|
||||
|
|
|
|||
132
src/managers/input/trackpad/gestures/ScrollMoveGesture.cpp
Normal file
132
src/managers/input/trackpad/gestures/ScrollMoveGesture.cpp
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#include "ScrollMoveGesture.hpp"
|
||||
|
||||
#include "../../../../desktop/state/FocusState.hpp"
|
||||
#include "../../../../desktop/Workspace.hpp"
|
||||
#include "../../../../helpers/Monitor.hpp"
|
||||
#include "../../../../layout/LayoutManager.hpp"
|
||||
#include "../../../../layout/algorithm/Algorithm.hpp"
|
||||
#include "../../../../layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp"
|
||||
#include "../../../../layout/space/Space.hpp"
|
||||
#include "../../../../config/ConfigValue.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
constexpr double SCROLL_GESTURE_VELOCITY_DECAY = 5.0;
|
||||
constexpr double SCROLL_GESTURE_MAX_PROJECTION = 2.0;
|
||||
constexpr double SCROLL_GESTURE_VELOCITY_SMOOTH = 0.35;
|
||||
|
||||
static Layout::Tiled::CScrollingAlgorithm* currentScrollingLayout() {
|
||||
const auto PMONITOR = Desktop::focusState()->monitor();
|
||||
if (!PMONITOR)
|
||||
return nullptr;
|
||||
|
||||
const auto PWORKSPACE = PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace;
|
||||
if (!PWORKSPACE || !PWORKSPACE->m_space)
|
||||
return nullptr;
|
||||
|
||||
const auto ALGORITHM = PWORKSPACE->m_space->algorithm();
|
||||
if (!ALGORITHM || !ALGORITHM->tiledAlgo())
|
||||
return nullptr;
|
||||
|
||||
return dynamic_cast<Layout::Tiled::CScrollingAlgorithm*>(ALGORITHM->tiledAlgo().get());
|
||||
}
|
||||
|
||||
static float deltaForUpdate(const ITrackpadGesture::STrackpadGestureUpdate& e) {
|
||||
if (!e.swipe)
|
||||
return 0.F;
|
||||
|
||||
switch (e.direction) {
|
||||
case TRACKPAD_GESTURE_DIR_LEFT:
|
||||
case TRACKPAD_GESTURE_DIR_RIGHT:
|
||||
case TRACKPAD_GESTURE_DIR_HORIZONTAL: return e.swipe->delta.x * e.scale;
|
||||
case TRACKPAD_GESTURE_DIR_UP:
|
||||
case TRACKPAD_GESTURE_DIR_DOWN:
|
||||
case TRACKPAD_GESTURE_DIR_VERTICAL: return e.swipe->delta.y * e.scale;
|
||||
default: return std::abs(e.swipe->delta.x) > std::abs(e.swipe->delta.y) ? e.swipe->delta.x * e.scale : e.swipe->delta.y * e.scale;
|
||||
}
|
||||
}
|
||||
|
||||
void CScrollMoveTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) {
|
||||
ITrackpadGesture::begin(e);
|
||||
|
||||
const auto LAYOUT = currentScrollingLayout();
|
||||
|
||||
m_wasScrollingLayout = !!LAYOUT;
|
||||
m_hasLastUpdate = false;
|
||||
m_lastUpdateTimeMs = 0;
|
||||
m_velocity = 0.0;
|
||||
|
||||
m_startedOffset = LAYOUT ? LAYOUT->normalizedTapeOffset() : 0.0;
|
||||
m_startedColumn = LAYOUT ? LAYOUT->currentColumn() : nullptr;
|
||||
}
|
||||
|
||||
void CScrollMoveTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) {
|
||||
if (!m_wasScrollingLayout)
|
||||
return;
|
||||
|
||||
const auto SCROLLING = currentScrollingLayout();
|
||||
if (!SCROLLING)
|
||||
return;
|
||||
|
||||
const float DELTA = deltaForUpdate(e);
|
||||
const double PRIMARY = SCROLLING->primaryViewportSize();
|
||||
|
||||
if (DELTA == 0.F || PRIMARY <= 0.0)
|
||||
return;
|
||||
|
||||
const double NORMALIZED_DELTA = DELTA / PRIMARY;
|
||||
const double NORMALIZED_OFFSET_DELTA = -NORMALIZED_DELTA;
|
||||
|
||||
SCROLLING->moveTapeNormalized(NORMALIZED_DELTA);
|
||||
|
||||
if (!e.swipe)
|
||||
return;
|
||||
|
||||
if (m_hasLastUpdate && e.swipe->timeMs > m_lastUpdateTimeMs) {
|
||||
const double DT = (e.swipe->timeMs - m_lastUpdateTimeMs) / 1000.0;
|
||||
|
||||
if (DT > 0.0) {
|
||||
const double INSTANT_VELOCITY = NORMALIZED_OFFSET_DELTA / DT;
|
||||
|
||||
if (std::isfinite(INSTANT_VELOCITY))
|
||||
m_velocity = m_velocity * (1.0 - SCROLL_GESTURE_VELOCITY_SMOOTH) + INSTANT_VELOCITY * SCROLL_GESTURE_VELOCITY_SMOOTH;
|
||||
}
|
||||
}
|
||||
|
||||
m_hasLastUpdate = true;
|
||||
m_lastUpdateTimeMs = e.swipe->timeMs;
|
||||
}
|
||||
|
||||
void CScrollMoveTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) {
|
||||
static auto PSNAP = CConfigValue<Config::BOOL>("gestures:scrolling:move_snap_to_grid");
|
||||
|
||||
const auto SCROLLING = currentScrollingLayout();
|
||||
if (!m_wasScrollingLayout || !SCROLLING) {
|
||||
m_wasScrollingLayout = false;
|
||||
m_hasLastUpdate = false;
|
||||
m_velocity = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
const bool CANCELLED = e.swipe && e.swipe->cancelled;
|
||||
const double Δ = (CANCELLED ? 0.0 : std::clamp(m_velocity / SCROLL_GESTURE_VELOCITY_DECAY, -SCROLL_GESTURE_MAX_PROJECTION, SCROLL_GESTURE_MAX_PROJECTION));
|
||||
const double PROJECTED = SCROLLING->normalizedTapeOffset() + Δ;
|
||||
|
||||
if (*PSNAP) {
|
||||
const auto LANDED = SCROLLING->snapToProjectedOffset(PROJECTED);
|
||||
|
||||
// if we land on the same thing we started, move the tape back
|
||||
if (LANDED == m_startedColumn && LANDED)
|
||||
SCROLLING->moveTapeNormalized(SCROLLING->normalizedTapeOffset() - m_startedOffset);
|
||||
else
|
||||
SCROLLING->focusColumn(LANDED);
|
||||
} else {
|
||||
SCROLLING->moveTape(Δ);
|
||||
SCROLLING->focusColumn(SCROLLING->getColumnAtViewportCenter());
|
||||
}
|
||||
|
||||
m_wasScrollingLayout = false;
|
||||
m_hasLastUpdate = false;
|
||||
m_velocity = 0.0;
|
||||
}
|
||||
27
src/managers/input/trackpad/gestures/ScrollMoveGesture.hpp
Normal file
27
src/managers/input/trackpad/gestures/ScrollMoveGesture.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include "ITrackpadGesture.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Layout::Tiled {
|
||||
struct SColumnData;
|
||||
}
|
||||
|
||||
class CScrollMoveTrackpadGesture : public ITrackpadGesture {
|
||||
public:
|
||||
CScrollMoveTrackpadGesture() = default;
|
||||
virtual ~CScrollMoveTrackpadGesture() = default;
|
||||
|
||||
virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e);
|
||||
virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e);
|
||||
virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e);
|
||||
|
||||
private:
|
||||
bool m_wasScrollingLayout = false;
|
||||
bool m_hasLastUpdate = false;
|
||||
uint32_t m_lastUpdateTimeMs = 0;
|
||||
double m_velocity = 0.0;
|
||||
double m_startedOffset = 0.0;
|
||||
WP<Layout::Tiled::SColumnData> m_startedColumn = nullptr;
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue