mirror of
https://github.com/hyprwm/Hyprland
synced 2026-05-07 15:58:02 +02:00
layout/scrolling: handle fullscreen manually
Makes scroll handle fullscreen by itself, allowing for fs scrolls.
This commit is contained in:
parent
336dc6c04e
commit
ab8caea9fb
30 changed files with 834 additions and 165 deletions
|
|
@ -176,7 +176,7 @@ TEST_CASE(scrollSwapcolWrapping) {
|
|||
}
|
||||
|
||||
TEST_CASE(scrollWindowRule) {
|
||||
OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
OK(getFromSocket("/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
NLog::log("{}Testing Scrolling Width", Colors::GREEN);
|
||||
|
||||
|
|
@ -195,5 +195,47 @@ TEST_CASE(scrollWindowRule) {
|
|||
ASSERT(Tests::windowCount(), 2);
|
||||
|
||||
// not the greatest test, but as long as res and gaps don't change, we good.
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 174,1036");
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 179,1036");
|
||||
}
|
||||
|
||||
TEST_CASE(scrollFullscreen) {
|
||||
OK(getFromSocket("/eval hl.config({ general = { layout = 'scrolling' } })"));
|
||||
|
||||
NLog::log("{}Testing Scrolling FS", Colors::GREEN);
|
||||
|
||||
ASSERT(!!Tests::spawnKitty("kitty_scroll_A"), true);
|
||||
ASSERT(!!Tests::spawnKitty("kitty_scroll_B"), true);
|
||||
ASSERT(!!Tests::spawnKitty("kitty_scroll_C"), true);
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({ window = \"class:kitty_scroll_B\" })"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.window.fullscreen()"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
ASSERT_CONTAINS(str, "size: 1920,1080");
|
||||
ASSERT_CONTAINS(str, "class: kitty_scroll_B");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({ direction = \"left\" })"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
ASSERT_CONTAINS(str, "class: kitty_scroll_A");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({ direction = \"right\" })"));
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({ direction = \"right\" })"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
ASSERT_CONTAINS(str, "class: kitty_scroll_C");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch hl.dsp.focus({ direction = \"left\" })"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
ASSERT_CONTAINS(str, "size: 1920,1080");
|
||||
ASSERT_CONTAINS(str, "class: kitty_scroll_B");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1462,10 +1462,10 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, Math::eDirection
|
|||
if (!PWORKSPACE)
|
||||
return nullptr; // ??
|
||||
|
||||
return getWindowInDirection(WINDOWIDEALBB, PWORKSPACE, dir, pWindow, pWindow->m_isFloating);
|
||||
return getWindowInDirection(WINDOWIDEALBB, PWORKSPACE, dir, pWindow->m_isFloating, pWindow, pWindow->m_isFloating);
|
||||
}
|
||||
|
||||
PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, PHLWINDOW ignoreWindow, bool useVectorAngles) {
|
||||
PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, bool floatingPreference, PHLWINDOW ignoreWindow, bool useVectorAngles) {
|
||||
if (dir == Math::DIRECTION_DEFAULT)
|
||||
return nullptr;
|
||||
|
||||
|
|
@ -1509,71 +1509,93 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks
|
|||
return overlap <= std::min(sizeA, sizeB) * MAX_OVERLAP_RATIO;
|
||||
};
|
||||
|
||||
for (auto const& w : m_windows) {
|
||||
if (w == ignoreWindow || !w->m_workspace || !w->m_isMapped || !w->acceptsInput() || (!w->isFullscreen() && w->m_isFloating) || !w->m_workspace->isVisible())
|
||||
continue;
|
||||
auto find = [&]() {
|
||||
for (auto const& w : m_windows) {
|
||||
if (w == ignoreWindow || !w->m_workspace || !w->m_isMapped || (!w->isFullscreen() && w->m_isFloating) || !w->m_workspace->isVisible())
|
||||
continue;
|
||||
|
||||
if (pWorkspace->m_monitor == w->m_monitor && pWorkspace != w->m_workspace)
|
||||
continue;
|
||||
if (w->isHidden())
|
||||
continue;
|
||||
|
||||
if (pWorkspace->m_hasFullscreenWindow && !w->isAllowedOverFullscreen())
|
||||
continue;
|
||||
// check if the input is blocked by anything except BELOW_FULLSCREEN
|
||||
if (w->isInputBlocked(INPUT_BLOCK_ALL & (~INPUT_BLOCK_BELOW_FULLSCREEN)))
|
||||
continue;
|
||||
|
||||
if (!*PMONITORFALLBACK && pWorkspace->m_monitor != w->m_monitor)
|
||||
continue;
|
||||
if (pWorkspace->m_monitor == w->m_monitor && pWorkspace != w->m_workspace)
|
||||
continue;
|
||||
|
||||
const auto BWINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved();
|
||||
if (pWorkspace->m_hasFullscreenWindow && !w->isAllowedOverFullscreen())
|
||||
continue;
|
||||
|
||||
const auto POSB = Vector2D(BWINDOWIDEALBB.x, BWINDOWIDEALBB.y);
|
||||
const auto SIZEB = Vector2D(BWINDOWIDEALBB.width, BWINDOWIDEALBB.height);
|
||||
if (!*PMONITORFALLBACK && pWorkspace->m_monitor != w->m_monitor)
|
||||
continue;
|
||||
|
||||
double intersectLength = -1;
|
||||
if (w->m_isFloating != floatingPreference)
|
||||
continue;
|
||||
|
||||
switch (dir) {
|
||||
case Math::DIRECTION_LEFT:
|
||||
if (isAdjacent(POSA.x, POSA.x + SIZEA.x, POSB.x, POSB.x + SIZEB.x))
|
||||
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
||||
break;
|
||||
case Math::DIRECTION_RIGHT:
|
||||
if (isAdjacent(POSB.x, POSB.x + SIZEB.x, POSA.x, POSA.x + SIZEA.x))
|
||||
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
||||
break;
|
||||
case Math::DIRECTION_UP:
|
||||
if (isAdjacent(POSA.y, POSA.y + SIZEA.y, POSB.y, POSB.y + SIZEB.y))
|
||||
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
||||
break;
|
||||
case Math::DIRECTION_DOWN:
|
||||
if (isAdjacent(POSB.y, POSB.y + SIZEB.y, POSA.y, POSA.y + SIZEA.y))
|
||||
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
const auto BWINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved();
|
||||
|
||||
if (*PMETHOD == 0 /* history */) {
|
||||
if (intersectLength > 0) {
|
||||
const auto POSB = Vector2D(BWINDOWIDEALBB.x, BWINDOWIDEALBB.y);
|
||||
const auto SIZEB = Vector2D(BWINDOWIDEALBB.width, BWINDOWIDEALBB.height);
|
||||
|
||||
// get idx
|
||||
int windowIDX = -1;
|
||||
const auto& HISTORY = Desktop::History::windowTracker()->fullHistory();
|
||||
for (int64_t i = HISTORY.size() - 1; i >= 0; --i) {
|
||||
if (HISTORY[i] == w) {
|
||||
windowIDX = i;
|
||||
break;
|
||||
double intersectLength = -1;
|
||||
|
||||
switch (dir) {
|
||||
case Math::DIRECTION_LEFT:
|
||||
if (isAdjacent(POSA.x, POSA.x + SIZEA.x, POSB.x, POSB.x + SIZEB.x))
|
||||
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
||||
break;
|
||||
case Math::DIRECTION_RIGHT:
|
||||
if (isAdjacent(POSB.x, POSB.x + SIZEB.x, POSA.x, POSA.x + SIZEA.x))
|
||||
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
||||
break;
|
||||
case Math::DIRECTION_UP:
|
||||
if (isAdjacent(POSA.y, POSA.y + SIZEA.y, POSB.y, POSB.y + SIZEB.y))
|
||||
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
||||
break;
|
||||
case Math::DIRECTION_DOWN:
|
||||
if (isAdjacent(POSB.y, POSB.y + SIZEB.y, POSA.y, POSA.y + SIZEA.y))
|
||||
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (*PMETHOD == 0 /* history */) {
|
||||
if (intersectLength > 0) {
|
||||
|
||||
// get idx
|
||||
int windowIDX = -1;
|
||||
const auto& HISTORY = Desktop::History::windowTracker()->fullHistory();
|
||||
for (int64_t i = HISTORY.size() - 1; i >= 0; --i) {
|
||||
if (HISTORY[i] == w) {
|
||||
windowIDX = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (windowIDX > leaderValue) {
|
||||
leaderValue = windowIDX;
|
||||
leaderWindow = w;
|
||||
}
|
||||
}
|
||||
|
||||
if (windowIDX > leaderValue) {
|
||||
leaderValue = windowIDX;
|
||||
} else /* length */ {
|
||||
if (intersectLength > leaderValue) {
|
||||
leaderValue = intersectLength;
|
||||
leaderWindow = w;
|
||||
}
|
||||
}
|
||||
} else /* length */ {
|
||||
if (intersectLength > leaderValue) {
|
||||
leaderValue = intersectLength;
|
||||
leaderWindow = w;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Find the window, then if we don't find one with preferred
|
||||
// float status, try the opposite.
|
||||
find();
|
||||
|
||||
if (!leaderWindow) {
|
||||
floatingPreference = !floatingPreference;
|
||||
find();
|
||||
}
|
||||
|
||||
} else {
|
||||
static const std::unordered_map<Math::eDirection, Vector2D> VECTORS = {
|
||||
{Math::DIRECTION_RIGHT, {1, 0}}, {Math::DIRECTION_UP, {0, -1}}, {Math::DIRECTION_DOWN, {0, 1}}, {Math::DIRECTION_LEFT, {-1, 0}}};
|
||||
|
|
@ -2248,9 +2270,16 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::Vie
|
|||
PWORKSPACE->m_fullscreenMode = NEW_EFFECTIVE_MODE;
|
||||
PWORKSPACE->m_hasFullscreenWindow = NEW_EFFECTIVE_MODE != FSMODE_NONE;
|
||||
|
||||
g_layoutManager->fullscreenRequestForTarget(PWINDOW->layoutTarget(), OLD_EFFECTIVE_MODE, NEW_EFFECTIVE_MODE);
|
||||
PWORKSPACE->setNoMembersAboveFullscreen();
|
||||
|
||||
PWINDOW->m_fullscreenState.internal = state.internal;
|
||||
const auto FULLSCREEN_REQUEST_RESULT = g_layoutManager->fullscreenRequestForTarget(PWINDOW->layoutTarget(), OLD_EFFECTIVE_MODE, NEW_EFFECTIVE_MODE);
|
||||
const bool LAYOUT_HANDLED_FULLSCREEN = FULLSCREEN_REQUEST_RESULT == Layout::FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT;
|
||||
|
||||
if (LAYOUT_HANDLED_FULLSCREEN) {
|
||||
PWORKSPACE->m_fullscreenMode = FSMODE_NONE;
|
||||
PWORKSPACE->m_hasFullscreenWindow = false;
|
||||
} else
|
||||
PWINDOW->m_fullscreenState.internal = state.internal;
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "fullscreen", .data = std::to_string(sc<int>(NEW_EFFECTIVE_MODE) != FSMODE_NONE)});
|
||||
Event::bus()->m_events.window.fullscreen.emit(PWINDOW);
|
||||
|
|
@ -2275,8 +2304,9 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::Vie
|
|||
ls->m_aboveFullscreen = false;
|
||||
}
|
||||
|
||||
g_pDesktopAnimationManager->setFullscreenFadeAnimation(
|
||||
PWORKSPACE, PWORKSPACE->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT);
|
||||
if (!LAYOUT_HANDLED_FULLSCREEN)
|
||||
g_pDesktopAnimationManager->setFullscreenFadeAnimation(
|
||||
PWORKSPACE, PWORKSPACE->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT);
|
||||
|
||||
PWINDOW->sendWindowSize(true);
|
||||
|
||||
|
|
@ -2290,7 +2320,7 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::Vie
|
|||
|
||||
// send a scanout tranche if we are entering fullscreen, and send a regular one if we aren't.
|
||||
// ignore if DS is disabled.
|
||||
if (*PDIRECTSCANOUT == 1 || (*PDIRECTSCANOUT == 2 && PWINDOW->getContentType() == CONTENT_TYPE_GAME)) {
|
||||
if (!LAYOUT_HANDLED_FULLSCREEN && (*PDIRECTSCANOUT == 1 || (*PDIRECTSCANOUT == 2 && PWINDOW->getContentType() == CONTENT_TYPE_GAME))) {
|
||||
auto surf = PWINDOW->getSolitaryResource();
|
||||
if (surf)
|
||||
g_pHyprRenderer->setSurfaceScanoutMode(surf, NEW_EFFECTIVE_MODE != FSMODE_NONE ? PMONITOR->m_self.lock() : nullptr);
|
||||
|
|
|
|||
|
|
@ -116,7 +116,8 @@ class CCompositor {
|
|||
void changeWindowZOrder(PHLWINDOW, bool);
|
||||
void cleanupFadingOut(const MONITORID& monid);
|
||||
PHLWINDOW getWindowInDirection(PHLWINDOW, Math::eDirection);
|
||||
PHLWINDOW getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, PHLWINDOW ignoreWindow = nullptr, bool useVectorAngles = false);
|
||||
PHLWINDOW getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, bool floatingPreference, PHLWINDOW ignoreWindow = nullptr,
|
||||
bool useVectorAngles = false);
|
||||
PHLWINDOW getWindowCycle(PHLWINDOW cur, bool focusableOnly = false, std::optional<bool> floating = std::nullopt, bool visible = false, bool prev = false,
|
||||
bool allowFullscreenBlocked = false);
|
||||
PHLWINDOW getWindowCycleHist(PHLWINDOWREF cur, bool focusableOnly = false, std::optional<bool> floating = std::nullopt, bool visible = false, bool next = false,
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@ ActionResult Actions::moveFocus(Math::eDirection dir) {
|
|||
}
|
||||
|
||||
const auto PWINDOWCANDIDATE = g_pCompositor->getWindowInDirection(box, PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace,
|
||||
dir, PLASTWINDOW, PLASTWINDOW->m_isFloating);
|
||||
dir, PLASTWINDOW->m_isFloating, PLASTWINDOW, PLASTWINDOW->m_isFloating);
|
||||
if (PWINDOWCANDIDATE)
|
||||
switchToWindow(PWINDOWCANDIDATE);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "Workspace.hpp"
|
||||
#include "view/Group.hpp"
|
||||
#include "view/LayerSurface.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "../config/shared/animation/AnimationTree.hpp"
|
||||
#include "../config/shared/workspace/WorkspaceRuleManager.hpp"
|
||||
|
|
@ -8,6 +9,7 @@
|
|||
#include "../managers/EventManager.hpp"
|
||||
#include "../helpers/Monitor.hpp"
|
||||
#include "../layout/space/Space.hpp"
|
||||
#include "../layout/target/Target.hpp"
|
||||
#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp"
|
||||
#include "../event/EventBus.hpp"
|
||||
|
||||
|
|
@ -539,7 +541,10 @@ void CWorkspace::rename(const std::string& name) {
|
|||
}
|
||||
|
||||
void CWorkspace::updateWindows() {
|
||||
m_hasFullscreenWindow = std::ranges::any_of(m_space->targets(), [](const auto& t) { return t->fullscreenMode() != FSMODE_NONE; });
|
||||
m_hasFullscreenWindow = std::ranges::any_of(m_space->targets(), [](const auto& t) { return t && t->fullscreenMode() != FSMODE_NONE && !t->layoutManagedFullscreen(); });
|
||||
|
||||
if (!m_hasFullscreenWindow)
|
||||
m_fullscreenMode = FSMODE_NONE;
|
||||
|
||||
for (auto const& t : m_space->targets()) {
|
||||
if (t->window())
|
||||
|
|
@ -562,3 +567,15 @@ void CWorkspace::setPersistent(bool persistent) {
|
|||
bool CWorkspace::isPersistent() {
|
||||
return m_persistent;
|
||||
}
|
||||
|
||||
void CWorkspace::setNoMembersAboveFullscreen() {
|
||||
// make all windows and layers on the same workspace under the fullscreen window
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (w->m_workspace == m_self && !w->isFullscreen() && !w->m_fadingOut && !w->m_pinned)
|
||||
w->m_createdOverFullscreen = false;
|
||||
}
|
||||
for (auto const& ls : g_pCompositor->m_layers) {
|
||||
if (ls->m_monitor == m_monitor)
|
||||
ls->m_aboveFullscreen = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ class CWorkspace {
|
|||
void updateWindows();
|
||||
void setPersistent(bool persistent);
|
||||
bool isPersistent();
|
||||
void setNoMembersAboveFullscreen();
|
||||
|
||||
struct {
|
||||
CSignalT<> destroy;
|
||||
|
|
|
|||
|
|
@ -95,6 +95,9 @@ void CFocusState::rawWindowFocus(PHLWINDOW pWindow, eFocusReason reason, SP<CWLS
|
|||
static auto PFOLLOWMOUSE = CConfigValue<Config::INTEGER>("input:follow_mouse");
|
||||
static auto PSPECIALFALLTHROUGH = CConfigValue<Config::INTEGER>("input:special_fallthrough");
|
||||
|
||||
if (pWindow == m_focusWindow && surface == m_focusSurface)
|
||||
return;
|
||||
|
||||
if (!pWindow || !pWindow->priorityFocus()) {
|
||||
if (g_pSessionLockManager->isSessionLocked()) {
|
||||
Log::logger->log(Log::DEBUG, "Refusing a keyboard focus to a window because of a sessionlock");
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
#include "../../managers/input/InputManager.hpp"
|
||||
#include "../../managers/PointerManager.hpp"
|
||||
#include "../../managers/animation/DesktopAnimationManager.hpp"
|
||||
#include "../../layout/algorithm/Algorithm.hpp"
|
||||
#include "../../layout/space/Space.hpp"
|
||||
#include "../../layout/LayoutManager.hpp"
|
||||
#include "../../layout/target/WindowTarget.hpp"
|
||||
|
|
@ -267,7 +268,7 @@ CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
|
|||
auto POS = m_position;
|
||||
auto SIZE = m_size;
|
||||
|
||||
if (isFullscreen()) {
|
||||
if (isFullscreen() && (!layoutTarget() || !layoutTarget()->layoutManagedFullscreen())) {
|
||||
POS = PMONITOR->m_position;
|
||||
SIZE = PMONITOR->m_size;
|
||||
|
||||
|
|
@ -748,12 +749,12 @@ bool CWindow::isInputBlocked() const {
|
|||
return m_inputBlockReasons != INPUT_BLOCK_NONE;
|
||||
}
|
||||
|
||||
bool CWindow::isInputBlocked(eWindowInputBlockReason reason) const {
|
||||
return (m_inputBlockReasons & sc<uint32_t>(reason)) != 0;
|
||||
bool CWindow::isInputBlocked(std::underlying_type_t<eWindowInputBlockReason> reasons) const {
|
||||
return (m_inputBlockReasons & reasons) != 0;
|
||||
}
|
||||
|
||||
bool CWindow::isInputBlockedOnly(eWindowInputBlockReason reason) const {
|
||||
return m_inputBlockReasons == sc<uint32_t>(reason);
|
||||
return m_inputBlockReasons == reason;
|
||||
}
|
||||
|
||||
bool CWindow::acceptsInput() const {
|
||||
|
|
@ -772,7 +773,13 @@ bool CWindow::isAllowedOverFullscreen() const {
|
|||
}
|
||||
|
||||
bool CWindow::isBlockedByFullscreen() const {
|
||||
if (!m_workspace || !m_workspace->m_hasFullscreenWindow)
|
||||
if (!m_workspace)
|
||||
return false;
|
||||
|
||||
const auto ALGORITHM = m_workspace->m_space ? m_workspace->m_space->algorithm() : nullptr;
|
||||
const bool HAS_LAYOUT_FULLSCREEN = ALGORITHM && ALGORITHM->layoutFullscreenCoversMonitor();
|
||||
|
||||
if (!m_workspace->m_hasFullscreenWindow && !HAS_LAYOUT_FULLSCREEN)
|
||||
return false;
|
||||
|
||||
return !isAllowedOverFullscreen();
|
||||
|
|
|
|||
|
|
@ -89,11 +89,13 @@ namespace Desktop::View {
|
|||
WINDOW_ALPHA_LAST,
|
||||
};
|
||||
|
||||
enum eWindowInputBlockReason : uint32_t {
|
||||
enum eWindowInputBlockReason : uint8_t {
|
||||
INPUT_BLOCK_NONE = 0,
|
||||
INPUT_BLOCK_GROUP_INACTIVE = 1 << 0,
|
||||
INPUT_BLOCK_MONOCLE_INACTIVE = 1 << 1,
|
||||
INPUT_BLOCK_BELOW_FULLSCREEN = 1 << 2,
|
||||
INPUT_BLOCK_GROUP_INACTIVE = (1 << 0),
|
||||
INPUT_BLOCK_MONOCLE_INACTIVE = (1 << 1),
|
||||
INPUT_BLOCK_BELOW_FULLSCREEN = (1 << 2),
|
||||
|
||||
INPUT_BLOCK_ALL = std::numeric_limits<std::underlying_type_t<eWindowInputBlockReason>>::max(),
|
||||
};
|
||||
|
||||
struct SWindowActiveEvent {
|
||||
|
|
@ -285,6 +287,11 @@ namespace Desktop::View {
|
|||
// For the noclosefor windowrule
|
||||
Time::steady_tp m_closeableSince = Time::steadyNow();
|
||||
|
||||
// layout-settable flags. These are reset when layout changes.
|
||||
struct {
|
||||
bool cantLockCursor = false;
|
||||
} m_layoutFlags;
|
||||
|
||||
// For the list lookup
|
||||
bool operator==(const CWindow& rhs) const {
|
||||
return m_xdgSurface == rhs.m_xdgSurface && m_xwaylandSurface == rhs.m_xwaylandSurface && m_position == rhs.m_position && m_size == rhs.m_size &&
|
||||
|
|
@ -314,7 +321,7 @@ namespace Desktop::View {
|
|||
bool isHidden() const;
|
||||
void setInputBlocked(eWindowInputBlockReason reason, bool blocked);
|
||||
bool isInputBlocked() const;
|
||||
bool isInputBlocked(eWindowInputBlockReason reason) const;
|
||||
bool isInputBlocked(std::underlying_type_t<eWindowInputBlockReason> reasons) const;
|
||||
bool isInputBlockedOnly(eWindowInputBlockReason reason) const;
|
||||
bool acceptsInput() const;
|
||||
bool isAllowedOverFullscreen() const;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
#include "../managers/input/InputManager.hpp"
|
||||
#include "../errorOverlay/Overlay.hpp"
|
||||
#include "../layout/LayoutManager.hpp"
|
||||
#include "../layout/space/Space.hpp"
|
||||
#include "../layout/algorithm/Algorithm.hpp"
|
||||
#include "../i18n/Engine.hpp"
|
||||
#include "../helpers/cm/ColorManagement.hpp"
|
||||
#include "time/Time.hpp"
|
||||
|
|
@ -1672,7 +1674,7 @@ uint32_t CMonitor::isSolitaryBlocked(bool full) {
|
|||
return reasons;
|
||||
}
|
||||
|
||||
if (!PWORKSPACE->m_hasFullscreenWindow) {
|
||||
if (!inFullscreenMode()) {
|
||||
reasons |= SC_WINDOWED;
|
||||
if (!full)
|
||||
return reasons;
|
||||
|
|
@ -1720,7 +1722,7 @@ uint32_t CMonitor::isSolitaryBlocked(bool full) {
|
|||
return reasons;
|
||||
}
|
||||
|
||||
const auto PCANDIDATE = PWORKSPACE->getFullscreenWindow();
|
||||
const auto PCANDIDATE = getFullscreenWindow();
|
||||
|
||||
if (!PCANDIDATE) {
|
||||
reasons |= SC_CANDIDATE;
|
||||
|
|
@ -1791,7 +1793,7 @@ void CMonitor::recheckSolitary() {
|
|||
if (isSolitaryBlocked())
|
||||
return;
|
||||
|
||||
m_solitaryClient = PWORKSPACE->getFullscreenWindow();
|
||||
m_solitaryClient = getFullscreenWindow();
|
||||
}
|
||||
|
||||
uint8_t CMonitor::isTearingBlocked(bool full) {
|
||||
|
|
@ -1871,11 +1873,12 @@ uint16_t CMonitor::isDSBlocked(bool full) {
|
|||
}
|
||||
|
||||
if (*PDIRECTSCANOUT == 2) {
|
||||
if (!PWORKSPACE || !PWORKSPACE->m_hasFullscreenWindow || PWORKSPACE->m_fullscreenMode != FSMODE_FULLSCREEN) {
|
||||
const auto FSWINDOW = getFullscreenWindow();
|
||||
if (!PWORKSPACE || !inFullscreenMode() || !FSWINDOW) {
|
||||
reasons |= DS_BLOCK_WINDOWED;
|
||||
if (!full)
|
||||
return reasons;
|
||||
} else if (PWORKSPACE->getFullscreenWindow()->getContentType() != CONTENT_TYPE_GAME) {
|
||||
} else if (FSWINDOW->getContentType() != CONTENT_TYPE_GAME) {
|
||||
reasons |= DS_BLOCK_CONTENT;
|
||||
if (!full)
|
||||
return reasons;
|
||||
|
|
@ -2268,17 +2271,34 @@ bool CMonitor::inHDR() {
|
|||
|
||||
bool CMonitor::inFullscreenMode() {
|
||||
// Check special workspace first since it renders on top of regular workspaces
|
||||
if (m_activeSpecialWorkspace && m_activeSpecialWorkspace->m_hasFullscreenWindow && m_activeSpecialWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN)
|
||||
if (m_activeSpecialWorkspace &&
|
||||
((m_activeSpecialWorkspace->m_hasFullscreenWindow && m_activeSpecialWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN) ||
|
||||
(m_activeSpecialWorkspace->m_space && m_activeSpecialWorkspace->m_space->algorithm() && m_activeSpecialWorkspace->m_space->algorithm()->layoutFullscreenCoversMonitor())))
|
||||
return true;
|
||||
return m_activeWorkspace && m_activeWorkspace->m_hasFullscreenWindow && m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN;
|
||||
return m_activeWorkspace &&
|
||||
((m_activeWorkspace->m_hasFullscreenWindow && m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN) ||
|
||||
(m_activeWorkspace->m_space && m_activeWorkspace->m_space->algorithm() && m_activeWorkspace->m_space->algorithm()->layoutFullscreenCoversMonitor()));
|
||||
}
|
||||
|
||||
PHLWINDOW CMonitor::getFullscreenWindow() {
|
||||
// Check special workspace first since it renders on top of regular workspaces
|
||||
if (m_activeSpecialWorkspace && m_activeSpecialWorkspace->m_hasFullscreenWindow && m_activeSpecialWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN)
|
||||
return m_activeSpecialWorkspace->getFullscreenWindow();
|
||||
|
||||
if (m_activeSpecialWorkspace && m_activeSpecialWorkspace->m_space && m_activeSpecialWorkspace->m_space->algorithm() &&
|
||||
m_activeSpecialWorkspace->m_space->algorithm()->layoutFullscreenCoversMonitor()) {
|
||||
const auto TARGET = m_activeSpecialWorkspace->m_space->algorithm()->layoutFullscreenTarget();
|
||||
return TARGET ? TARGET->window() : nullptr;
|
||||
}
|
||||
|
||||
if (m_activeWorkspace && m_activeWorkspace->m_hasFullscreenWindow && m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN)
|
||||
return m_activeWorkspace->getFullscreenWindow();
|
||||
|
||||
if (m_activeWorkspace && m_activeWorkspace->m_space && m_activeWorkspace->m_space->algorithm() && m_activeWorkspace->m_space->algorithm()->layoutFullscreenCoversMonitor()) {
|
||||
const auto TARGET = m_activeWorkspace->m_space->algorithm()->layoutFullscreenTarget();
|
||||
return TARGET ? TARGET->window() : nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,9 +94,11 @@ void CLayoutManager::endDragTarget() {
|
|||
m_dragStateController->dragEnd();
|
||||
}
|
||||
|
||||
void CLayoutManager::fullscreenRequestForTarget(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode) {
|
||||
if (target->space())
|
||||
target->space()->setFullscreen(target, effectiveMode);
|
||||
eFullscreenRequestResult CLayoutManager::fullscreenRequestForTarget(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode) {
|
||||
if (target && target->space())
|
||||
return target->space()->setFullscreen(target, currentEffectiveMode, effectiveMode);
|
||||
|
||||
return FULLSCREEN_REQUEST_DEFAULT;
|
||||
}
|
||||
|
||||
void CLayoutManager::switchTargets(SP<ITarget> a, SP<ITarget> b, bool preserveFocus) {
|
||||
|
|
|
|||
|
|
@ -40,43 +40,48 @@ namespace Layout {
|
|||
SNAP_RIGHT = (1 << 3),
|
||||
};
|
||||
|
||||
enum eFullscreenRequestResult : uint8_t {
|
||||
FULLSCREEN_REQUEST_DEFAULT = 0,
|
||||
FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT,
|
||||
};
|
||||
|
||||
class CLayoutManager {
|
||||
public:
|
||||
CLayoutManager();
|
||||
~CLayoutManager() = default;
|
||||
|
||||
void newTarget(SP<ITarget> target, SP<CSpace> space);
|
||||
void removeTarget(SP<ITarget> target);
|
||||
void newTarget(SP<ITarget> target, SP<CSpace> space);
|
||||
void removeTarget(SP<ITarget> target);
|
||||
|
||||
void changeFloatingMode(SP<ITarget> target);
|
||||
void changeFloatingMode(SP<ITarget> target);
|
||||
|
||||
void beginDragTarget(SP<ITarget> target, eMouseBindMode mode);
|
||||
void moveMouse(const Vector2D& mousePos);
|
||||
void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
||||
void setTargetGeom(const CBox& box, SP<ITarget> target); // floats only
|
||||
void endDragTarget();
|
||||
void beginDragTarget(SP<ITarget> target, eMouseBindMode mode);
|
||||
void moveMouse(const Vector2D& mousePos);
|
||||
void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
||||
void setTargetGeom(const CBox& box, SP<ITarget> target); // floats only
|
||||
void endDragTarget();
|
||||
|
||||
Config::ErrorResult layoutMsg(const std::string_view& sv);
|
||||
Config::ErrorResult layoutMsg(const std::string_view& sv);
|
||||
|
||||
void fullscreenRequestForTarget(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode);
|
||||
eFullscreenRequestResult fullscreenRequestForTarget(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode);
|
||||
|
||||
void switchTargets(SP<ITarget> a, SP<ITarget> b, bool preserveFocus = true);
|
||||
void switchTargets(SP<ITarget> a, SP<ITarget> b, bool preserveFocus = true);
|
||||
|
||||
void moveInDirection(SP<ITarget> target, const std::string& direction, bool silent = false);
|
||||
void moveInDirection(SP<ITarget> target, const std::string& direction, bool silent = false);
|
||||
|
||||
SP<ITarget> getNextCandidate(SP<CSpace> space, SP<ITarget> from);
|
||||
SP<ITarget> getNextCandidate(SP<CSpace> space, SP<ITarget> from);
|
||||
|
||||
bool isReachable(SP<ITarget> target);
|
||||
bool isReachable(SP<ITarget> target);
|
||||
|
||||
void bringTargetToTop(SP<ITarget> target);
|
||||
void bringTargetToTop(SP<ITarget> target);
|
||||
|
||||
std::optional<Vector2D> predictSizeForNewTiledTarget();
|
||||
std::optional<Vector2D> predictSizeForNewTiledTarget();
|
||||
|
||||
void performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP<ITarget> target, eMouseBindMode mode, int corner, const Vector2D& beginSize);
|
||||
void performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP<ITarget> target, eMouseBindMode mode, int corner, const Vector2D& beginSize);
|
||||
|
||||
void invalidateMonitorGeometries(PHLMONITOR);
|
||||
void recalculateMonitor(PHLMONITOR);
|
||||
void invalidateMonitorGeometries(PHLMONITOR);
|
||||
void recalculateMonitor(PHLMONITOR);
|
||||
|
||||
const UP<Supplementary::CDragStateController>& dragController();
|
||||
|
||||
|
|
|
|||
|
|
@ -154,6 +154,25 @@ void CAlgorithm::moveTarget(const Vector2D& Δ, SP<ITarget> target) {
|
|||
m_floating->moveTarget(Δ, target);
|
||||
}
|
||||
|
||||
eFullscreenRequestResult CAlgorithm::requestFullscreen(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode) {
|
||||
if (!target)
|
||||
return FULLSCREEN_REQUEST_DEFAULT;
|
||||
|
||||
const SFullscreenRequest request = {.target = target, .currentEffectiveMode = currentEffectiveMode, .effectiveMode = effectiveMode};
|
||||
return target->floating() ? m_floating->requestFullscreen(request) : m_tiled->requestFullscreen(request);
|
||||
}
|
||||
|
||||
SP<ITarget> CAlgorithm::layoutFullscreenTarget() const {
|
||||
if (const auto TARGET = m_tiled->layoutFullscreenTarget(); TARGET)
|
||||
return TARGET;
|
||||
|
||||
return m_floating->layoutFullscreenTarget();
|
||||
}
|
||||
|
||||
bool CAlgorithm::layoutFullscreenCoversMonitor() const {
|
||||
return m_tiled->layoutFullscreenCoversMonitor() || m_floating->layoutFullscreenCoversMonitor();
|
||||
}
|
||||
|
||||
void CAlgorithm::swapTargets(SP<ITarget> a, SP<ITarget> b) {
|
||||
auto swapFirst = [&a, &b](std::vector<WP<ITarget>>& targets) -> bool {
|
||||
auto ia = std::ranges::find(targets, a);
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ namespace Layout {
|
|||
|
||||
void setTargetGeom(const CBox& box, SP<ITarget> target); // only for float
|
||||
|
||||
eFullscreenRequestResult requestFullscreen(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode);
|
||||
SP<ITarget> layoutFullscreenTarget() const;
|
||||
bool layoutFullscreenCoversMonitor() const;
|
||||
|
||||
void updateFloatingAlgo(UP<IFloatingAlgorithm>&& algo);
|
||||
void updateTiledAlgo(UP<ITiledAlgorithm>&& algo);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,19 @@ std::optional<Vector2D> IModeAlgorithm::predictSizeForNewTarget() {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
eFullscreenRequestResult IModeAlgorithm::requestFullscreen(const SFullscreenRequest& request) {
|
||||
(void)request;
|
||||
return FULLSCREEN_REQUEST_DEFAULT;
|
||||
}
|
||||
|
||||
SP<ITarget> IModeAlgorithm::layoutFullscreenTarget() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool IModeAlgorithm::layoutFullscreenCoversMonitor() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<Vector2D> IModeAlgorithm::focalPointForDir(SP<ITarget> t, Math::eDirection dir) {
|
||||
Vector2D focalPoint;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@ namespace Layout {
|
|||
class ITarget;
|
||||
class CAlgorithm;
|
||||
|
||||
struct SFullscreenRequest {
|
||||
SP<ITarget> target;
|
||||
eFullscreenMode currentEffectiveMode = static_cast<eFullscreenMode>(0);
|
||||
eFullscreenMode effectiveMode = static_cast<eFullscreenMode>(0);
|
||||
};
|
||||
|
||||
class IModeAlgorithm {
|
||||
public:
|
||||
virtual ~IModeAlgorithm() = default;
|
||||
|
|
@ -44,6 +50,13 @@ namespace Layout {
|
|||
// optional: predict new window's size
|
||||
virtual std::optional<Vector2D> predictSizeForNewTarget();
|
||||
|
||||
// optional: allow algorithms to own fullscreen semantics for a target.
|
||||
virtual eFullscreenRequestResult requestFullscreen(const SFullscreenRequest& request);
|
||||
|
||||
// optional: expose an algorithm-owned fullscreen target and whether it is monitor-exclusive.
|
||||
virtual SP<ITarget> layoutFullscreenTarget() const;
|
||||
virtual bool layoutFullscreenCoversMonitor() const;
|
||||
|
||||
// Impl'd here: focal point for dir
|
||||
virtual std::optional<Vector2D> focalPointForDir(SP<ITarget> t, Math::eDirection dir);
|
||||
|
||||
|
|
|
|||
|
|
@ -147,14 +147,10 @@ double CScrollTapeController::calculateStripSize(size_t stripIndex, const CBox&
|
|||
return usablePrimary * m_strips[stripIndex].size;
|
||||
}
|
||||
|
||||
CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne) {
|
||||
CBox CScrollTapeController::calculateStripBox(size_t stripIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne) {
|
||||
if (stripIndex >= m_strips.size())
|
||||
return {};
|
||||
|
||||
const auto& strip = m_strips[stripIndex];
|
||||
if (targetIndex >= strip.targetSizes.size())
|
||||
return {};
|
||||
|
||||
const double usableSecondary = getSecondary(usableArea.size());
|
||||
const double usablePrimary = getPrimary(usableArea.size());
|
||||
const double cameraOffset = calculateCameraOffset(usableArea, fullscreenOnOne);
|
||||
|
|
@ -163,13 +159,6 @@ CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetI
|
|||
double primaryPos = calculateStripStart(stripIndex, usableArea, fullscreenOnOne);
|
||||
double primarySize = calculateStripSize(stripIndex, usableArea, fullscreenOnOne);
|
||||
|
||||
// calculate position along secondary axis (within strip)
|
||||
double secondaryPos = 0.0;
|
||||
for (size_t i = 0; i < targetIndex; ++i) {
|
||||
secondaryPos += strip.targetSizes[i] * usableSecondary;
|
||||
}
|
||||
double secondarySize = strip.targetSizes[targetIndex] * usableSecondary;
|
||||
|
||||
// apply camera offset based on direction
|
||||
// for RIGHT/DOWN: scroll offset moves content left/up (subtract)
|
||||
// for LEFT/UP: scroll offset moves content right/down (different coordinate system)
|
||||
|
|
@ -185,8 +174,8 @@ CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetI
|
|||
}
|
||||
|
||||
// create the box in primary/secondary coordinates
|
||||
Vector2D pos = makeVector(primaryPos, secondaryPos);
|
||||
Vector2D size = makeVector(primarySize, secondarySize);
|
||||
Vector2D pos = makeVector(primaryPos, 0.0);
|
||||
Vector2D size = makeVector(primarySize, usableSecondary);
|
||||
|
||||
// translate to workspace position
|
||||
pos = pos + workspaceOffset;
|
||||
|
|
@ -194,6 +183,35 @@ CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetI
|
|||
return CBox{pos, size};
|
||||
}
|
||||
|
||||
CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne) {
|
||||
if (stripIndex >= m_strips.size())
|
||||
return {};
|
||||
|
||||
const auto& strip = m_strips[stripIndex];
|
||||
if (targetIndex >= strip.targetSizes.size())
|
||||
return {};
|
||||
|
||||
CBox stripBox = calculateStripBox(stripIndex, usableArea, workspaceOffset, fullscreenOnOne);
|
||||
const double usableSecondary = getSecondary(usableArea.size());
|
||||
|
||||
double secondaryPos = 0.0;
|
||||
for (size_t i = 0; i < targetIndex; ++i) {
|
||||
secondaryPos += strip.targetSizes[i] * usableSecondary;
|
||||
}
|
||||
|
||||
const double secondarySize = strip.targetSizes[targetIndex] * usableSecondary;
|
||||
|
||||
if (isPrimaryHorizontal()) {
|
||||
stripBox.y = workspaceOffset.y + secondaryPos;
|
||||
stripBox.h = secondarySize;
|
||||
} else {
|
||||
stripBox.x = workspaceOffset.x + secondaryPos;
|
||||
stripBox.w = secondarySize;
|
||||
}
|
||||
|
||||
return stripBox;
|
||||
}
|
||||
|
||||
double CScrollTapeController::calculateCameraOffset(const CBox& usableArea, bool fullscreenOnOne) {
|
||||
const double maxExtent = calculateMaxExtent(usableArea, fullscreenOnOne);
|
||||
const double usablePrimary = getPrimary(usableArea.size());
|
||||
|
|
@ -275,13 +293,14 @@ size_t CScrollTapeController::getStripAtCenter(const CBox& usableArea, bool full
|
|||
return 0;
|
||||
|
||||
const double usablePrimary = getPrimary(usableArea.size());
|
||||
double currentPos = m_offset;
|
||||
const double viewCenter = m_offset + usablePrimary / 2.0;
|
||||
double currentEnd = 0.0;
|
||||
|
||||
for (size_t i = 0; i < m_strips.size(); ++i) {
|
||||
const double stripSize = calculateStripSize(i, usableArea, fullscreenOnOne);
|
||||
currentPos += stripSize;
|
||||
currentEnd += stripSize;
|
||||
|
||||
if (currentPos >= usablePrimary / 2.0 - 2.0)
|
||||
if (currentEnd >= viewCenter - 2.0)
|
||||
return i;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ namespace Layout::Tiled {
|
|||
double calculateStripStart(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false) const;
|
||||
double calculateStripSize(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false) const;
|
||||
|
||||
CBox calculateStripBox(size_t stripIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne = false);
|
||||
CBox calculateTargetBox(size_t stripIndex, size_t targetIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne = false);
|
||||
|
||||
double calculateCameraOffset(const CBox& usableArea, bool fullscreenOnOne = false);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "../../../../config/shared/workspace/WorkspaceRuleManager.hpp"
|
||||
#include "../../../../render/Renderer.hpp"
|
||||
#include "../../../../managers/input/InputManager.hpp"
|
||||
#include "../../../../managers/animation/DesktopAnimationManager.hpp"
|
||||
#include "../../../../event/EventBus.hpp"
|
||||
|
||||
#include <hyprutils/string/VarList2.hpp>
|
||||
|
|
@ -427,23 +428,83 @@ void SScrollingData::recalculate(bool forceInstant) {
|
|||
algorithm->m_parent->space()->workspace()->m_hasFullscreenWindow)
|
||||
return;
|
||||
|
||||
algorithm->syncFullscreenTargets();
|
||||
|
||||
static const auto PFSONONE = CConfigValue<Config::INTEGER>("scrolling:fullscreen_on_one_column");
|
||||
|
||||
const CBox USABLE = algorithm->usableArea();
|
||||
const auto WORKAREA = algorithm->m_parent->space()->workArea();
|
||||
const CBox MONBOX = algorithm->m_parent->space()->workspace()->m_monitor->logicalBox();
|
||||
|
||||
const auto WORKSPACERULE = Config::workspaceRuleMgr()->getWorkspaceRuleFor(algorithm->m_parent->space()->workspace());
|
||||
static auto PGAPSINDATA = CConfigValue<Config::IComplexConfigValue>("general:gaps_in");
|
||||
auto* const PGAPSIN = sc<Config::CCssGapData*>((PGAPSINDATA.ptr()));
|
||||
const auto GAPSIN = (WORKSPACERULE && WORKSPACERULE->m_gapsIn.has_value()) ? WORKSPACERULE->m_gapsIn.value() : *PGAPSIN;
|
||||
|
||||
bool anyFullscreenCovers = false;
|
||||
for (const auto& COL : columns) {
|
||||
if (algorithm->fullscreenTargetDataForColumn(COL) && algorithm->fullscreenColumnCoversMonitor(COL)) {
|
||||
anyFullscreenCovers = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
controller->setDirection(algorithm->getDynamicDirection());
|
||||
algorithm->updateFullscreenFade(anyFullscreenCovers);
|
||||
|
||||
const auto targetBoxWithGaps = [&](const CBox& logical, size_t colIdx, size_t targetIdx, bool fullscreenOrHidden) -> STargetBox {
|
||||
if (fullscreenOrHidden)
|
||||
return {.logicalBox = logical, .visualBox = logical};
|
||||
|
||||
CBox visual = logical;
|
||||
const bool PRIMARY_HORIZ = controller->isPrimaryHorizontal();
|
||||
|
||||
const bool GAP_LEFT = PRIMARY_HORIZ ? colIdx > 0 : targetIdx > 0;
|
||||
const bool GAP_RIGHT = PRIMARY_HORIZ ? colIdx + 1 < columns.size() : targetIdx + 1 < columns[colIdx]->targetDatas.size();
|
||||
const bool GAP_TOP = PRIMARY_HORIZ ? targetIdx > 0 : colIdx > 0;
|
||||
const bool GAP_BOTTOM = PRIMARY_HORIZ ? targetIdx + 1 < columns[colIdx]->targetDatas.size() : colIdx + 1 < columns.size();
|
||||
|
||||
const auto GAPOFFSETTOPLEFT = Vector2D(sc<double>(GAP_LEFT ? GAPSIN.m_left : 0), sc<double>(GAP_TOP ? GAPSIN.m_top : 0));
|
||||
const auto GAPOFFSETBOTTOMRIGHT = Vector2D(sc<double>(GAP_RIGHT ? GAPSIN.m_right : 0), sc<double>(GAP_BOTTOM ? GAPSIN.m_bottom : 0));
|
||||
|
||||
visual.x += GAPOFFSETTOPLEFT.x;
|
||||
visual.y += GAPOFFSETTOPLEFT.y;
|
||||
visual.w = std::max(1.0, visual.w - GAPOFFSETTOPLEFT.x - GAPOFFSETBOTTOMRIGHT.x);
|
||||
visual.h = std::max(1.0, visual.h - GAPOFFSETTOPLEFT.y - GAPOFFSETBOTTOMRIGHT.y);
|
||||
|
||||
return {.logicalBox = logical, .visualBox = visual};
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < columns.size(); ++i) {
|
||||
const auto& COL = columns[i];
|
||||
const auto FS = algorithm->fullscreenTargetDataForColumn(COL);
|
||||
|
||||
for (size_t j = 0; j < COL->targetDatas.size(); ++j) {
|
||||
const auto& TARGET = COL->targetDatas[j];
|
||||
|
||||
TARGET->layoutBox = controller->calculateTargetBox(i, j, USABLE, WORKAREA.pos(), *PFSONONE);
|
||||
if (FS) {
|
||||
if (TARGET == FS) {
|
||||
if (algorithm->fullscreenColumnCoversMonitor(COL))
|
||||
TARGET->layoutBox = MONBOX;
|
||||
else {
|
||||
TARGET->layoutBox = controller->calculateStripBox(i, USABLE, WORKAREA.pos(), *PFSONONE);
|
||||
|
||||
if (controller->isPrimaryHorizontal()) {
|
||||
TARGET->layoutBox.y = MONBOX.y;
|
||||
TARGET->layoutBox.h = MONBOX.h;
|
||||
} else {
|
||||
TARGET->layoutBox.x = MONBOX.x;
|
||||
TARGET->layoutBox.w = MONBOX.w;
|
||||
}
|
||||
}
|
||||
} else
|
||||
TARGET->layoutBox = CBox{WORKAREA.pos() - Vector2D{100000.0, 100000.0}, Vector2D{1.0, 1.0}};
|
||||
} else
|
||||
TARGET->layoutBox = controller->calculateTargetBox(i, j, USABLE, WORKAREA.pos(), *PFSONONE);
|
||||
|
||||
if (TARGET->target)
|
||||
TARGET->target->setPositionGlobal(TARGET->layoutBox);
|
||||
TARGET->target->setPositionGlobal(targetBoxWithGaps(TARGET->layoutBox, i, j, FS));
|
||||
|
||||
if (forceInstant && TARGET->target)
|
||||
TARGET->target->warpPositionSize();
|
||||
}
|
||||
|
|
@ -544,6 +605,9 @@ CScrollingAlgorithm::CScrollingAlgorithm() {
|
|||
}
|
||||
|
||||
CScrollingAlgorithm::~CScrollingAlgorithm() {
|
||||
clearFullscreenTarget();
|
||||
updateFullscreenFade(false);
|
||||
|
||||
m_configCallback.reset();
|
||||
m_focusCallback.reset();
|
||||
}
|
||||
|
|
@ -631,6 +695,8 @@ void CScrollingAlgorithm::removeTarget(SP<ITarget> target) {
|
|||
if (!DATA)
|
||||
return;
|
||||
|
||||
clearFullscreenTarget(target);
|
||||
|
||||
if (!m_scrollingData->next(DATA->column.lock()) && DATA->column->targetDatas.size() <= 1) {
|
||||
// move the view if this is the last column
|
||||
const auto USABLE = usableArea();
|
||||
|
|
@ -814,6 +880,303 @@ void CScrollingAlgorithm::recalculate() {
|
|||
m_scrollingData->recalculate();
|
||||
}
|
||||
|
||||
void CScrollingAlgorithm::syncFullscreenTargets() {
|
||||
for (auto it = m_fullscreenTargets.begin(); it != m_fullscreenTargets.end();) {
|
||||
const auto TARGET = it->target.lock();
|
||||
|
||||
if (!TARGET || !TARGET->layoutManagedFullscreen() || TARGET->fullscreenMode() != FSMODE_FULLSCREEN || TARGET->space() != m_parent->space()) {
|
||||
it = m_fullscreenTargets.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto TDATA = dataFor(TARGET);
|
||||
if (!TDATA) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto COL = TDATA->column.lock())
|
||||
COL->setColumnWidth(fullscreenColumnWidth());
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
for (const auto& COL : m_scrollingData->columns) {
|
||||
for (const auto& TDATA : COL->targetDatas) {
|
||||
const auto TARGET = TDATA->target.lock();
|
||||
if (!TARGET || !TARGET->layoutManagedFullscreen() || TARGET->fullscreenMode() != FSMODE_FULLSCREEN || TARGET->space() != m_parent->space())
|
||||
continue;
|
||||
|
||||
if (!fullscreenStateForTarget(TARGET))
|
||||
m_fullscreenTargets.emplace_back(SFullscreenScrollState{.target = TARGET, .restoreColumnWidth = COL ? std::optional<float>{COL->getColumnWidth()} : std::nullopt});
|
||||
|
||||
COL->setColumnWidth(fullscreenColumnWidth());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CScrollingAlgorithm::SFullscreenScrollState* CScrollingAlgorithm::fullscreenStateForTarget(SP<ITarget> target) {
|
||||
if (!target)
|
||||
return nullptr;
|
||||
|
||||
for (auto& state : m_fullscreenTargets) {
|
||||
if (state.target.lock() == target)
|
||||
return &state;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CScrollingAlgorithm::SFullscreenScrollState* CScrollingAlgorithm::fullscreenStateForData(SP<SScrollingTargetData> target) {
|
||||
if (!target)
|
||||
return nullptr;
|
||||
|
||||
return fullscreenStateForTarget(target->target.lock());
|
||||
}
|
||||
|
||||
void CScrollingAlgorithm::expelTarget(SP<SScrollingTargetData> tdata, SP<SColumnData> srcCol, std::optional<int64_t> insertIdx) {
|
||||
auto col = !insertIdx ? m_scrollingData->add() : m_scrollingData->add(*insertIdx);
|
||||
srcCol->remove(tdata->target.lock());
|
||||
col->add(tdata);
|
||||
m_scrollingData->centerOrFitCol(col);
|
||||
}
|
||||
|
||||
eFullscreenRequestResult CScrollingAlgorithm::requestFullscreen(const SFullscreenRequest& request) {
|
||||
if (!request.target || !m_parent || request.target->space() != m_parent->space())
|
||||
return FULLSCREEN_REQUEST_DEFAULT;
|
||||
|
||||
const auto TDATA = dataFor(request.target);
|
||||
if (!TDATA)
|
||||
return FULLSCREEN_REQUEST_DEFAULT;
|
||||
|
||||
if (request.effectiveMode == FSMODE_FULLSCREEN) {
|
||||
if (!fullscreenStateForTarget(request.target)) {
|
||||
const auto COL = TDATA->column.lock();
|
||||
m_fullscreenTargets.emplace_back(
|
||||
SFullscreenScrollState{.target = request.target, .restoreColumnWidth = COL ? std::optional<float>{COL->getColumnWidth()} : std::nullopt});
|
||||
}
|
||||
|
||||
if (const auto COL = TDATA->column.lock()) {
|
||||
COL->setColumnWidth(fullscreenColumnWidth());
|
||||
m_scrollingData->centerOrFitCol(COL);
|
||||
}
|
||||
|
||||
request.target->setFullscreenMode(FSMODE_FULLSCREEN);
|
||||
|
||||
return FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT;
|
||||
} else if (request.effectiveMode == FSMODE_MAXIMIZED) {
|
||||
// expel, then max width
|
||||
const auto CURRENT_COL = TDATA->column.lock();
|
||||
|
||||
if (CURRENT_COL->targetDatas.size() > 1) {
|
||||
const auto lastTarget = CURRENT_COL->targetDatas.back();
|
||||
const auto currentIdx = m_scrollingData->idx(CURRENT_COL);
|
||||
const auto NEXT_COL = m_scrollingData->next(CURRENT_COL);
|
||||
const auto insertIdx = !NEXT_COL ? std::nullopt : std::optional<int64_t>{currentIdx};
|
||||
|
||||
expelTarget(lastTarget, CURRENT_COL, insertIdx);
|
||||
|
||||
TDATA->column->setColumnWidth(1.F);
|
||||
} else
|
||||
CURRENT_COL->setColumnWidth(1.F);
|
||||
|
||||
request.target->setFullscreenMode(FSMODE_NONE);
|
||||
|
||||
return FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT;
|
||||
}
|
||||
|
||||
if (isFullscreenTarget(TDATA) || request.target->layoutManagedFullscreen()) {
|
||||
clearFullscreenTarget(request.target);
|
||||
request.target->setFullscreenMode(FSMODE_NONE);
|
||||
return request.effectiveMode == FSMODE_NONE ? FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT : FULLSCREEN_REQUEST_DEFAULT;
|
||||
}
|
||||
|
||||
return FULLSCREEN_REQUEST_DEFAULT;
|
||||
}
|
||||
|
||||
SP<ITarget> CScrollingAlgorithm::layoutFullscreenTarget() const {
|
||||
SP<SScrollingTargetData> fallback;
|
||||
|
||||
for (const auto& COL : m_scrollingData->columns) {
|
||||
for (const auto& TDATA : COL->targetDatas) {
|
||||
if (!isFullscreenTarget(TDATA))
|
||||
continue;
|
||||
|
||||
if (!fallback)
|
||||
fallback = TDATA;
|
||||
|
||||
if (fullscreenColumnCoversMonitor(TDATA->column.lock()))
|
||||
return TDATA->target.lock();
|
||||
}
|
||||
}
|
||||
|
||||
return fallback ? fallback->target.lock() : nullptr;
|
||||
}
|
||||
|
||||
bool CScrollingAlgorithm::layoutFullscreenCoversMonitor() const {
|
||||
for (const auto& COL : m_scrollingData->columns) {
|
||||
for (const auto& TDATA : COL->targetDatas) {
|
||||
if (!isFullscreenTarget(TDATA))
|
||||
continue;
|
||||
|
||||
if (fullscreenColumnCoversMonitor(TDATA->column.lock()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SP<SScrollingTargetData> CScrollingAlgorithm::fullscreenTargetDataForColumn(SP<SColumnData> col) const {
|
||||
if (!col)
|
||||
return nullptr;
|
||||
|
||||
for (const auto& TDATA : col->targetDatas) {
|
||||
if (!isFullscreenTarget(TDATA))
|
||||
continue;
|
||||
|
||||
return TDATA;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CScrollingAlgorithm::isFullscreenTarget(SP<SScrollingTargetData> target) const {
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
const auto TARGET = target->target.lock();
|
||||
if (!TARGET || !TARGET->layoutManagedFullscreen() || TARGET->fullscreenMode() == FSMODE_NONE)
|
||||
return false;
|
||||
|
||||
return dataFor(TARGET) == target;
|
||||
}
|
||||
|
||||
float CScrollingAlgorithm::fullscreenColumnWidth() const {
|
||||
if (!m_parent || !m_parent->space() || !m_parent->space()->workspace() || !m_parent->space()->workspace()->m_monitor || !m_scrollingData || !m_scrollingData->controller)
|
||||
return 1.F;
|
||||
|
||||
const auto USABLE = usableArea();
|
||||
const auto MONBOX = m_parent->space()->workspace()->m_monitor->logicalBox();
|
||||
const bool PRIMARY_HORIZ = m_scrollingData->controller->isPrimaryHorizontal();
|
||||
const double usablePrimary = PRIMARY_HORIZ ? USABLE.w : USABLE.h;
|
||||
const double monitorPrimary = PRIMARY_HORIZ ? MONBOX.w : MONBOX.h;
|
||||
|
||||
if (usablePrimary <= 0.0)
|
||||
return 1.F;
|
||||
|
||||
return std::max(1.F, sc<float>(monitorPrimary / usablePrimary));
|
||||
}
|
||||
|
||||
bool CScrollingAlgorithm::fullscreenColumnCoversMonitor(SP<SColumnData> col) const {
|
||||
if (!col || !m_scrollingData || !m_scrollingData->controller || !m_parent || !m_parent->space() || !m_parent->space()->workspace() ||
|
||||
!m_parent->space()->workspace()->m_monitor)
|
||||
return false;
|
||||
|
||||
if (!fullscreenTargetDataForColumn(col))
|
||||
return false;
|
||||
|
||||
const int64_t COL_IDX = m_scrollingData->idx(col);
|
||||
if (COL_IDX < 0)
|
||||
return false;
|
||||
|
||||
static const auto PFSONONE = CConfigValue<Config::INTEGER>("scrolling:fullscreen_on_one_column");
|
||||
|
||||
const auto USABLE = usableArea();
|
||||
const bool PRIMARY_HORIZ = m_scrollingData->controller->isPrimaryHorizontal();
|
||||
const double VIEW_SIZE = PRIMARY_HORIZ ? USABLE.w : USABLE.h;
|
||||
const double VIEW_START = m_scrollingData->controller->getOffset();
|
||||
const double VIEW_END = VIEW_START + VIEW_SIZE;
|
||||
const double COL_START = m_scrollingData->controller->calculateStripStart(COL_IDX, USABLE, *PFSONONE);
|
||||
const double COL_END = COL_START + m_scrollingData->controller->calculateStripSize(COL_IDX, USABLE, *PFSONONE);
|
||||
|
||||
return COL_START <= VIEW_START + 1.0 && COL_END >= VIEW_END - 1.0;
|
||||
}
|
||||
|
||||
void CScrollingAlgorithm::updateFullscreenFade(bool coversMonitor) {
|
||||
if (m_lastFullscreenCover == coversMonitor)
|
||||
return;
|
||||
|
||||
m_lastFullscreenCover = coversMonitor;
|
||||
|
||||
if (!coversMonitor) {
|
||||
// prevent stuck focus
|
||||
g_pInputManager->unconstrainMouse();
|
||||
for (const auto& fs : m_fullscreenTargets) {
|
||||
if (!fs.target || !fs.target->window())
|
||||
continue;
|
||||
|
||||
auto w = fs.target->window();
|
||||
|
||||
w->m_layoutFlags.cantLockCursor = true;
|
||||
}
|
||||
} else {
|
||||
for (const auto& fs : m_fullscreenTargets) {
|
||||
if (!fs.target || !fs.target->window())
|
||||
continue;
|
||||
|
||||
auto w = fs.target->window();
|
||||
|
||||
w->m_layoutFlags.cantLockCursor = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_parent || !m_parent->space() || !m_parent->space()->workspace())
|
||||
return;
|
||||
|
||||
// properly update things on top / bottom
|
||||
m_parent->space()->workspace()->setNoMembersAboveFullscreen();
|
||||
|
||||
g_pDesktopAnimationManager->setFullscreenFadeAnimation(m_parent->space()->workspace(),
|
||||
coversMonitor ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT);
|
||||
}
|
||||
|
||||
void CScrollingAlgorithm::clearFullscreenTarget(SP<ITarget> target) {
|
||||
bool cleared = false;
|
||||
|
||||
auto clear = [&](SP<ITarget> t) {
|
||||
t->setLayoutManagedFullscreen(false);
|
||||
if (t->window())
|
||||
t->window()->m_layoutFlags.cantLockCursor = false;
|
||||
cleared = true;
|
||||
};
|
||||
|
||||
for (auto it = m_fullscreenTargets.begin(); it != m_fullscreenTargets.end();) {
|
||||
const auto TARGET = it->target.lock();
|
||||
|
||||
if (!TARGET || (target && TARGET != target)) {
|
||||
if (!TARGET)
|
||||
it = m_fullscreenTargets.erase(it);
|
||||
else
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto TDATA = dataFor(TARGET);
|
||||
|
||||
clear(TARGET);
|
||||
|
||||
if (const auto COL = TDATA ? TDATA->column.lock() : nullptr; COL && it->restoreColumnWidth)
|
||||
COL->setColumnWidth(*it->restoreColumnWidth);
|
||||
|
||||
it = m_fullscreenTargets.erase(it);
|
||||
}
|
||||
|
||||
if (target && target->layoutManagedFullscreen())
|
||||
clear(target);
|
||||
else if (!target) {
|
||||
for (const auto& COL : m_scrollingData->columns) {
|
||||
for (const auto& TDATA : COL->targetDatas) {
|
||||
const auto TARGET = TDATA->target.lock();
|
||||
if (!TARGET || !TARGET->layoutManagedFullscreen())
|
||||
continue;
|
||||
|
||||
clear(TARGET);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SP<SScrollingTargetData> CScrollingAlgorithm::closestNode(const Vector2D& posGlobglobgabgalab) {
|
||||
SP<SScrollingTargetData> res = nullptr;
|
||||
double distClosest = -1;
|
||||
|
|
@ -1372,14 +1735,6 @@ Config::ErrorResult CScrollingAlgorithm::layoutMsg(const std::string_view& sv) {
|
|||
if (!CURRENT_COL)
|
||||
return stateErr("no current col");
|
||||
|
||||
// expel a target from srcCol into its own new column at insertIdx
|
||||
auto expelTarget = [&](SP<SScrollingTargetData> tdata, SP<SColumnData> srcCol, std::optional<int64_t> insertIdx) {
|
||||
auto col = !insertIdx ? m_scrollingData->add() : m_scrollingData->add(*insertIdx);
|
||||
srcCol->remove(tdata->target.lock());
|
||||
col->add(tdata);
|
||||
m_scrollingData->centerOrFitCol(col);
|
||||
};
|
||||
|
||||
// consume the first target from adjCol into dstCol
|
||||
auto consumeTarget = [&](SP<SColumnData> dstCol, SP<SColumnData> adjCol) {
|
||||
const auto target = adjCol->targetDatas.front();
|
||||
|
|
@ -1546,7 +1901,7 @@ SP<SScrollingTargetData> CScrollingAlgorithm::findBestNeighbor(SP<SScrollingTarg
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
SP<SScrollingTargetData> CScrollingAlgorithm::dataFor(SP<ITarget> t) {
|
||||
SP<SScrollingTargetData> CScrollingAlgorithm::dataFor(SP<ITarget> t) const {
|
||||
if (!t)
|
||||
return nullptr;
|
||||
|
||||
|
|
@ -1584,7 +1939,7 @@ eScrollDirection CScrollingAlgorithm::getDynamicDirection() {
|
|||
return SCROLL_DIR_RIGHT; // default
|
||||
}
|
||||
|
||||
CBox CScrollingAlgorithm::usableArea() {
|
||||
CBox CScrollingAlgorithm::usableArea() const {
|
||||
if (!m_parent || !m_parent->space())
|
||||
return {};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "ScrollTapeController.hpp"
|
||||
#include "../../../../helpers/signal/Signal.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace Layout::Tiled {
|
||||
|
|
@ -94,23 +95,27 @@ namespace Layout::Tiled {
|
|||
CScrollingAlgorithm();
|
||||
virtual ~CScrollingAlgorithm();
|
||||
|
||||
virtual void newTarget(SP<ITarget> target);
|
||||
virtual void movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
virtual void removeTarget(SP<ITarget> target);
|
||||
virtual void newTarget(SP<ITarget> target);
|
||||
virtual void movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
virtual void removeTarget(SP<ITarget> target);
|
||||
|
||||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
virtual void recalculate();
|
||||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
virtual void recalculate();
|
||||
|
||||
virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
|
||||
virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
|
||||
|
||||
virtual Config::ErrorResult layoutMsg(const std::string_view& sv);
|
||||
virtual std::optional<Vector2D> predictSizeForNewTarget();
|
||||
virtual Config::ErrorResult layoutMsg(const std::string_view& sv);
|
||||
virtual std::optional<Vector2D> predictSizeForNewTarget();
|
||||
|
||||
virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
|
||||
virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
|
||||
virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
|
||||
CBox usableArea();
|
||||
SP<SScrollingTargetData> dataFor(SP<ITarget> t);
|
||||
virtual eFullscreenRequestResult requestFullscreen(const SFullscreenRequest& request);
|
||||
virtual SP<ITarget> layoutFullscreenTarget() const;
|
||||
virtual bool layoutFullscreenCoversMonitor() const;
|
||||
|
||||
CBox usableArea() const;
|
||||
SP<SScrollingTargetData> dataFor(SP<ITarget> t) const;
|
||||
|
||||
enum eInputMode : uint8_t {
|
||||
INPUT_MODE_SOFT = 0,
|
||||
|
|
@ -129,16 +134,36 @@ namespace Layout::Tiled {
|
|||
std::vector<float> configuredWidths;
|
||||
} m_config;
|
||||
|
||||
eScrollDirection getDynamicDirection();
|
||||
eScrollDirection getDynamicDirection();
|
||||
|
||||
SP<SScrollingTargetData> findBestNeighbor(SP<SScrollingTargetData> pCurrent, SP<SColumnData> pTargetCol);
|
||||
SP<SScrollingTargetData> closestNode(const Vector2D& posGlobglobgabgalab);
|
||||
struct SFullscreenScrollState {
|
||||
WP<ITarget> target;
|
||||
std::optional<float> restoreColumnWidth;
|
||||
};
|
||||
|
||||
void focusTargetUpdate(SP<ITarget> target);
|
||||
void moveTargetTo(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
void focusOnInput(SP<ITarget> target, eInputMode input);
|
||||
void syncFullscreenTargets();
|
||||
SFullscreenScrollState* fullscreenStateForTarget(SP<ITarget> target);
|
||||
SFullscreenScrollState* fullscreenStateForData(SP<SScrollingTargetData> target);
|
||||
SP<SScrollingTargetData> fullscreenTargetDataForColumn(SP<SColumnData> col) const;
|
||||
bool isFullscreenTarget(SP<SScrollingTargetData> target) const;
|
||||
float fullscreenColumnWidth() const;
|
||||
bool fullscreenColumnCoversMonitor(SP<SColumnData> col) const;
|
||||
void updateFullscreenFade(bool coversMonitor);
|
||||
void clearFullscreenTarget(SP<ITarget> target = nullptr);
|
||||
|
||||
float defaultColumnWidth();
|
||||
SP<SScrollingTargetData> findBestNeighbor(SP<SScrollingTargetData> pCurrent, SP<SColumnData> pTargetCol);
|
||||
SP<SScrollingTargetData> closestNode(const Vector2D& posGlobglobgabgalab);
|
||||
|
||||
void focusTargetUpdate(SP<ITarget> target);
|
||||
void moveTargetTo(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
void focusOnInput(SP<ITarget> target, eInputMode input);
|
||||
|
||||
void expelTarget(SP<SScrollingTargetData> tdata, SP<SColumnData> srcCol, std::optional<int64_t> insertIdx);
|
||||
|
||||
float defaultColumnWidth();
|
||||
|
||||
std::vector<SFullscreenScrollState> m_fullscreenTargets;
|
||||
bool m_lastFullscreenCover = false;
|
||||
|
||||
friend struct SScrollingData;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -153,13 +153,29 @@ void CSpace::recalculate() {
|
|||
m_algorithm->recalculate();
|
||||
}
|
||||
|
||||
void CSpace::setFullscreen(SP<ITarget> t, eFullscreenMode mode) {
|
||||
t->setFullscreenMode(mode);
|
||||
eFullscreenRequestResult CSpace::setFullscreen(SP<ITarget> t, eFullscreenMode currentEffectiveMode, eFullscreenMode mode) {
|
||||
if (!t)
|
||||
return FULLSCREEN_REQUEST_DEFAULT;
|
||||
|
||||
const auto REQUEST_RESULT = m_algorithm ? m_algorithm->requestFullscreen(t, currentEffectiveMode, mode) : FULLSCREEN_REQUEST_DEFAULT;
|
||||
|
||||
t->setLayoutManagedFullscreen(REQUEST_RESULT == FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT && mode == FSMODE_FULLSCREEN);
|
||||
if (REQUEST_RESULT != FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT)
|
||||
t->setFullscreenMode(mode);
|
||||
|
||||
if (REQUEST_RESULT == FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT) {
|
||||
if (const auto WORKSPACE = workspace()) {
|
||||
WORKSPACE->m_fullscreenMode = FSMODE_NONE;
|
||||
WORKSPACE->m_hasFullscreenWindow = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == FSMODE_NONE && m_algorithm && t->floating())
|
||||
m_algorithm->recenter(t);
|
||||
|
||||
recalculate();
|
||||
|
||||
return REQUEST_RESULT;
|
||||
}
|
||||
|
||||
Config::ErrorResult CSpace::layoutMsg(const std::string_view& sv) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace Layout {
|
|||
|
||||
void setAlgorithmProvider(SP<CAlgorithm> algo);
|
||||
void recheckWorkArea();
|
||||
void setFullscreen(SP<ITarget> t, eFullscreenMode mode);
|
||||
eFullscreenRequestResult setFullscreen(SP<ITarget> t, eFullscreenMode currentEffectiveMode, eFullscreenMode mode);
|
||||
|
||||
void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,14 @@ void ITarget::setSpaceGhost(const SP<CSpace>& space) {
|
|||
m_ghostSpace = true;
|
||||
}
|
||||
|
||||
bool ITarget::layoutManagedFullscreen() const {
|
||||
return m_layoutManagedFullscreen;
|
||||
}
|
||||
|
||||
void ITarget::setLayoutManagedFullscreen(bool enabled) {
|
||||
m_layoutManagedFullscreen = enabled;
|
||||
}
|
||||
|
||||
SP<CSpace> ITarget::space() const {
|
||||
return m_space;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ namespace Layout {
|
|||
virtual void setPseudoSize(const Vector2D& size);
|
||||
virtual Vector2D pseudoSize();
|
||||
virtual void swap(SP<ITarget> b);
|
||||
virtual bool layoutManagedFullscreen() const;
|
||||
virtual void setLayoutManagedFullscreen(bool enabled);
|
||||
|
||||
//
|
||||
virtual bool floating() = 0;
|
||||
|
|
@ -77,9 +79,10 @@ namespace Layout {
|
|||
SP<CSpace> m_space;
|
||||
WP<ITarget> m_self;
|
||||
Vector2D m_floatingSize;
|
||||
bool m_pseudo = false;
|
||||
bool m_ghostSpace = false; // ghost space means a target belongs to a space, but isn't sent to the layout
|
||||
Vector2D m_pseudoSize = {1280, 720};
|
||||
bool m_wasTiling = false;
|
||||
bool m_pseudo = false;
|
||||
bool m_ghostSpace = false; // ghost space means a target belongs to a space, but isn't sent to the layout
|
||||
Vector2D m_pseudoSize = {1280, 720};
|
||||
bool m_wasTiling = false;
|
||||
bool m_layoutManagedFullscreen = false;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -68,6 +68,18 @@ void CWindowGroupTarget::setFullscreenMode(eFullscreenMode mode) {
|
|||
m_group->current()->m_fullscreenState.internal = mode;
|
||||
}
|
||||
|
||||
bool CWindowGroupTarget::layoutManagedFullscreen() const {
|
||||
return m_group->current()->m_target->layoutManagedFullscreen();
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::setLayoutManagedFullscreen(bool enabled) {
|
||||
ITarget::setLayoutManagedFullscreen(enabled);
|
||||
|
||||
for (const auto& w : m_group->windows()) {
|
||||
w->m_target->setLayoutManagedFullscreen(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CWindowGroupTarget::minSize() {
|
||||
return m_group->current()->minSize();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ namespace Layout {
|
|||
virtual void damageEntire();
|
||||
virtual void warpPositionSize();
|
||||
virtual void onUpdateSpace();
|
||||
virtual bool layoutManagedFullscreen() const;
|
||||
virtual void setLayoutManagedFullscreen(bool enabled);
|
||||
|
||||
private:
|
||||
CWindowGroupTarget(SP<Desktop::View::CGroup> g);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ void CWindowTarget::updatePos() {
|
|||
if (!m_space)
|
||||
return;
|
||||
|
||||
if (fullscreenMode() == FSMODE_FULLSCREEN)
|
||||
if (fullscreenMode() == FSMODE_FULLSCREEN && !layoutManagedFullscreen())
|
||||
return;
|
||||
|
||||
if (floating() && fullscreenMode() != FSMODE_MAXIMIZED) {
|
||||
|
|
@ -83,7 +83,24 @@ void CWindowTarget::updatePos() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (fullscreenMode() == FSMODE_FULLSCREEN)
|
||||
if (fullscreenMode() == FSMODE_FULLSCREEN && layoutManagedFullscreen()) {
|
||||
CBox nodeBox = m_box.logicalBox;
|
||||
CBox visualBox = m_box.visualBox.empty() ? nodeBox : m_box.visualBox;
|
||||
nodeBox.round();
|
||||
visualBox.round();
|
||||
|
||||
m_window->m_size = nodeBox.size();
|
||||
m_window->m_position = nodeBox.pos();
|
||||
|
||||
*m_window->m_realSize = visualBox.size();
|
||||
*m_window->m_realPosition = visualBox.pos();
|
||||
|
||||
m_window->updateWindowDecos();
|
||||
m_window->sendWindowSize();
|
||||
return;
|
||||
}
|
||||
|
||||
if (fullscreenMode() == FSMODE_FULLSCREEN && !layoutManagedFullscreen())
|
||||
return;
|
||||
|
||||
g_pHyprRenderer->damageWindow(window());
|
||||
|
|
@ -206,6 +223,7 @@ void CWindowTarget::updatePos() {
|
|||
}
|
||||
|
||||
m_window->updateWindowDecos();
|
||||
m_window->sendWindowSize();
|
||||
}
|
||||
|
||||
void CWindowTarget::assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint) {
|
||||
|
|
@ -214,6 +232,8 @@ void CWindowTarget::assignToSpace(const SP<CSpace>& space, std::optional<Vector2
|
|||
return;
|
||||
}
|
||||
|
||||
m_window->m_layoutFlags = {};
|
||||
|
||||
// keep the ref here so that moveToWorkspace doesn't unref the workspace
|
||||
// and assignToSpace doesn't think this is a new target because space wp is dead
|
||||
const auto WSREF = space->workspace();
|
||||
|
|
@ -236,6 +256,8 @@ void CWindowTarget::setFloating(bool x) {
|
|||
if (x == m_window->m_isFloating)
|
||||
return;
|
||||
|
||||
m_window->m_layoutFlags = {};
|
||||
|
||||
m_window->m_isFloating = x;
|
||||
m_window->m_pinned = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -487,7 +487,7 @@ void CDesktopAnimationManager::setFullscreenFadeAnimation(PHLWORKSPACE ws, eAnim
|
|||
if (ws->m_id == PMONITOR->activeWorkspaceID() || ws->m_id == PMONITOR->activeSpecialWorkspaceID()) {
|
||||
for (auto const& ls : PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
|
||||
if (!ls->m_fadingOut && !ls->m_aboveFullscreen)
|
||||
*ls->m_alpha = FULLSCREEN && ws->m_fullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f;
|
||||
*ls->m_alpha = FULLSCREEN && ws->m_fullscreenMode != FSMODE_MAXIMIZED ? 0.f : 1.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -453,7 +453,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st
|
|||
return pWindowIdeal;
|
||||
};
|
||||
|
||||
if (PWORKSPACE->m_hasFullscreenWindow && PWORKSPACE->m_fullscreenMode == FSMODE_FULLSCREEN) {
|
||||
const bool HAS_EXCLUSIVE_FULLSCREEN = (PWORKSPACE->m_hasFullscreenWindow && PWORKSPACE->m_fullscreenMode == FSMODE_FULLSCREEN) || PMONITOR->inFullscreenMode();
|
||||
|
||||
if (HAS_EXCLUSIVE_FULLSCREEN) {
|
||||
const auto IS_LS_UNFOCUSABLE = pFoundLayerSurface &&
|
||||
(pFoundLayerSurface->m_layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP ||
|
||||
(pFoundLayerSurface->m_layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && !pFoundLayerSurface->m_aboveFullscreen));
|
||||
|
|
@ -1741,7 +1743,17 @@ void CInputManager::unconstrainMouse() {
|
|||
bool CInputManager::isConstrained() {
|
||||
return std::ranges::any_of(m_constraints, [](auto const& c) {
|
||||
const auto constraint = c.lock();
|
||||
return constraint && constraint->isActive() && constraint->owner()->resource() == Desktop::focusState()->surface();
|
||||
|
||||
if (!constraint || !constraint->isActive() || constraint->owner()->resource() != Desktop::focusState()->surface())
|
||||
return false;
|
||||
|
||||
const auto OWNER = constraint->owner()->view();
|
||||
const auto WINDOW = Desktop::View::CWindow::fromView(OWNER);
|
||||
|
||||
if (!WINDOW)
|
||||
return false;
|
||||
|
||||
return !WINDOW->m_layoutFlags.cantLockCursor;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1749,7 +1761,15 @@ bool CInputManager::isLocked() {
|
|||
if (!isConstrained())
|
||||
return false;
|
||||
|
||||
const auto SURF = Desktop::View::CWLSurface::fromResource(Desktop::focusState()->surface());
|
||||
const auto SURF = Desktop::View::CWLSurface::fromResource(Desktop::focusState()->surface());
|
||||
|
||||
if (SURF) {
|
||||
const auto WINDOW = Desktop::View::CWindow::fromView(SURF->view());
|
||||
|
||||
if (WINDOW && WINDOW->m_layoutFlags.cantLockCursor)
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto CONSTRAINT = SURF ? SURF->constraint() : nullptr;
|
||||
|
||||
return CONSTRAINT && CONSTRAINT->isLocked();
|
||||
|
|
|
|||
|
|
@ -127,9 +127,11 @@ void CPointerConstraint::activate() {
|
|||
|
||||
// TODO: hack, probably not a super duper great idea
|
||||
if (g_pSeatManager->m_state.pointerFocus != m_hlSurface->resource()) {
|
||||
const auto SURFBOX = m_hlSurface->getSurfaceBoxGlobal();
|
||||
const auto LOCAL = SURFBOX.has_value() ? logicPositionHint() - SURFBOX->pos() : Vector2D{};
|
||||
g_pSeatManager->setPointerFocus(m_hlSurface->resource(), LOCAL);
|
||||
if (const auto W = Desktop::View::CWindow::fromView(m_hlSurface->view()); !W || !W->m_layoutFlags.cantLockCursor) {
|
||||
const auto SURFBOX = m_hlSurface->getSurfaceBoxGlobal();
|
||||
const auto LOCAL = SURFBOX.has_value() ? logicPositionHint() - SURFBOX->pos() : Vector2D{};
|
||||
g_pSeatManager->setPointerFocus(m_hlSurface->resource(), LOCAL);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_locked)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue