layout: rethonk layouts from the ground up (#12890)
Rewrites layouts to be much smaller, and deal with much less annoying BS. Improves the overall architecture, unifies handling of pseudotiling, and various other improvements.
This commit is contained in:
parent
51f8849e54
commit
723870337f
82 changed files with 8431 additions and 5527 deletions
146
src/layout/target/Target.cpp
Normal file
146
src/layout/target/Target.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#include "Target.hpp"
|
||||
#include "../space/Space.hpp"
|
||||
#include "../../debug/log/Logger.hpp"
|
||||
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
|
||||
using namespace Layout;
|
||||
using namespace Hyprutils::Utils;
|
||||
|
||||
void ITarget::setPositionGlobal(const CBox& box) {
|
||||
m_box = box;
|
||||
m_box.round();
|
||||
}
|
||||
|
||||
void ITarget::assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint) {
|
||||
if (m_space == space && !m_ghostSpace)
|
||||
return;
|
||||
|
||||
const bool HAD_SPACE = !!m_space;
|
||||
|
||||
if (m_space && !m_ghostSpace)
|
||||
m_space->remove(m_self.lock());
|
||||
|
||||
m_space = space;
|
||||
|
||||
if (space && HAD_SPACE)
|
||||
space->move(m_self.lock(), focalPoint);
|
||||
else if (space)
|
||||
space->add(m_self.lock());
|
||||
|
||||
if (!space)
|
||||
Log::logger->log(Log::WARN, "ITarget: assignToSpace with a null space?");
|
||||
|
||||
m_ghostSpace = false;
|
||||
|
||||
onUpdateSpace();
|
||||
}
|
||||
|
||||
void ITarget::setSpaceGhost(const SP<CSpace>& space) {
|
||||
if (m_space)
|
||||
assignToSpace(nullptr);
|
||||
|
||||
m_space = space;
|
||||
|
||||
m_ghostSpace = true;
|
||||
}
|
||||
|
||||
SP<CSpace> ITarget::space() const {
|
||||
return m_space;
|
||||
}
|
||||
|
||||
PHLWORKSPACE ITarget::workspace() const {
|
||||
if (!m_space)
|
||||
return nullptr;
|
||||
|
||||
return m_space->workspace();
|
||||
}
|
||||
|
||||
CBox ITarget::position() const {
|
||||
return m_box;
|
||||
}
|
||||
|
||||
void ITarget::rememberFloatingSize(const Vector2D& size) {
|
||||
m_floatingSize = size;
|
||||
}
|
||||
|
||||
Vector2D ITarget::lastFloatingSize() const {
|
||||
return m_floatingSize;
|
||||
}
|
||||
|
||||
void ITarget::recalc() {
|
||||
setPositionGlobal(m_box);
|
||||
}
|
||||
|
||||
void ITarget::setPseudo(bool x) {
|
||||
if (m_pseudo == x)
|
||||
return;
|
||||
|
||||
m_pseudo = x;
|
||||
|
||||
recalc();
|
||||
}
|
||||
|
||||
bool ITarget::isPseudo() const {
|
||||
return m_pseudo;
|
||||
}
|
||||
|
||||
void ITarget::setPseudoSize(const Vector2D& size) {
|
||||
m_pseudoSize = size;
|
||||
|
||||
recalc();
|
||||
}
|
||||
|
||||
Vector2D ITarget::pseudoSize() {
|
||||
return m_pseudoSize;
|
||||
}
|
||||
|
||||
void ITarget::swap(SP<ITarget> b) {
|
||||
const auto IS_FLOATING = floating();
|
||||
const auto IS_FLOATING_B = b->floating();
|
||||
|
||||
// Keep workspaces alive during a swap: moving one window will unref the ws
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
const auto PWS1 = workspace();
|
||||
// NOLINTNEXTLINE
|
||||
const auto PWS2 = b->workspace();
|
||||
|
||||
CScopeGuard x([&] {
|
||||
b->setFloating(IS_FLOATING);
|
||||
setFloating(IS_FLOATING_B);
|
||||
|
||||
// update the spaces
|
||||
b->onUpdateSpace();
|
||||
onUpdateSpace();
|
||||
});
|
||||
|
||||
if (b->space() == m_space) {
|
||||
// simplest
|
||||
m_space->swap(m_self.lock(), b);
|
||||
m_space->recalculate();
|
||||
return;
|
||||
}
|
||||
|
||||
// spaces differ
|
||||
if (m_space)
|
||||
m_space->swap(m_self.lock(), b);
|
||||
if (b->space())
|
||||
b->space()->swap(b, m_self.lock());
|
||||
|
||||
std::swap(m_space, b->m_space);
|
||||
|
||||
// recalc both
|
||||
if (m_space)
|
||||
m_space->recalculate();
|
||||
if (b->space())
|
||||
b->space()->recalculate();
|
||||
}
|
||||
|
||||
bool ITarget::wasTiling() const {
|
||||
return m_wasTiling;
|
||||
}
|
||||
|
||||
void ITarget::setWasTiling(bool x) {
|
||||
m_wasTiling = x;
|
||||
}
|
||||
79
src/layout/target/Target.hpp
Normal file
79
src/layout/target/Target.hpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../helpers/math/Math.hpp"
|
||||
#include "../../helpers/memory/Memory.hpp"
|
||||
#include "../../desktop/Workspace.hpp"
|
||||
|
||||
#include <expected>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Layout {
|
||||
enum eTargetType : uint8_t {
|
||||
TARGET_TYPE_WINDOW = 0,
|
||||
TARGET_TYPE_GROUP,
|
||||
};
|
||||
|
||||
enum eGeometryFailure : uint8_t {
|
||||
GEOMETRY_NO_DESIRED = 0,
|
||||
GEOMETRY_INVALID_DESIRED = 1,
|
||||
};
|
||||
|
||||
class CSpace;
|
||||
|
||||
struct SGeometryRequested {
|
||||
Vector2D size;
|
||||
std::optional<Vector2D> pos;
|
||||
};
|
||||
|
||||
class ITarget {
|
||||
public:
|
||||
virtual ~ITarget() = default;
|
||||
|
||||
virtual eTargetType type() = 0;
|
||||
|
||||
// position is within its space
|
||||
virtual void setPositionGlobal(const CBox& box);
|
||||
virtual CBox position() const;
|
||||
virtual void assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
virtual void setSpaceGhost(const SP<CSpace>& space);
|
||||
virtual SP<CSpace> space() const;
|
||||
virtual PHLWORKSPACE workspace() const;
|
||||
virtual PHLWINDOW window() const = 0;
|
||||
virtual void recalc();
|
||||
virtual bool wasTiling() const;
|
||||
virtual void setWasTiling(bool x);
|
||||
|
||||
virtual void rememberFloatingSize(const Vector2D& size);
|
||||
virtual Vector2D lastFloatingSize() const;
|
||||
|
||||
virtual void setPseudo(bool x);
|
||||
virtual bool isPseudo() const;
|
||||
virtual void setPseudoSize(const Vector2D& size);
|
||||
virtual Vector2D pseudoSize();
|
||||
virtual void swap(SP<ITarget> b);
|
||||
|
||||
//
|
||||
virtual bool floating() = 0;
|
||||
virtual void setFloating(bool x) = 0;
|
||||
virtual std::expected<SGeometryRequested, eGeometryFailure> desiredGeometry() = 0;
|
||||
virtual eFullscreenMode fullscreenMode() = 0;
|
||||
virtual void setFullscreenMode(eFullscreenMode mode) = 0;
|
||||
virtual std::optional<Vector2D> minSize() = 0;
|
||||
virtual std::optional<Vector2D> maxSize() = 0;
|
||||
virtual void damageEntire() = 0;
|
||||
virtual void warpPositionSize() = 0;
|
||||
virtual void onUpdateSpace() = 0;
|
||||
|
||||
protected:
|
||||
ITarget() = default;
|
||||
|
||||
CBox m_box;
|
||||
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;
|
||||
};
|
||||
};
|
||||
92
src/layout/target/WindowGroupTarget.cpp
Normal file
92
src/layout/target/WindowGroupTarget.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#include "WindowGroupTarget.hpp"
|
||||
|
||||
#include "../space/Space.hpp"
|
||||
#include "../algorithm/Algorithm.hpp"
|
||||
#include "WindowTarget.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
#include "../../render/Renderer.hpp"
|
||||
|
||||
using namespace Layout;
|
||||
|
||||
SP<CWindowGroupTarget> CWindowGroupTarget::create(SP<Desktop::View::CGroup> g) {
|
||||
auto target = SP<CWindowGroupTarget>(new CWindowGroupTarget(g));
|
||||
target->m_self = target;
|
||||
return target;
|
||||
}
|
||||
|
||||
CWindowGroupTarget::CWindowGroupTarget(SP<Desktop::View::CGroup> g) : m_group(g) {
|
||||
;
|
||||
}
|
||||
|
||||
eTargetType CWindowGroupTarget::type() {
|
||||
return TARGET_TYPE_GROUP;
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::setPositionGlobal(const CBox& box) {
|
||||
ITarget::setPositionGlobal(box);
|
||||
|
||||
updatePos();
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::updatePos() {
|
||||
for (const auto& w : m_group->windows()) {
|
||||
w->m_target->setPositionGlobal(m_box);
|
||||
}
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint) {
|
||||
ITarget::assignToSpace(space, focalPoint);
|
||||
|
||||
m_group->updateWorkspace(space->workspace());
|
||||
}
|
||||
|
||||
bool CWindowGroupTarget::floating() {
|
||||
return m_group->current()->m_target->floating();
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::setFloating(bool x) {
|
||||
for (const auto& w : m_group->windows()) {
|
||||
w->m_target->setFloating(x);
|
||||
}
|
||||
}
|
||||
|
||||
std::expected<SGeometryRequested, eGeometryFailure> CWindowGroupTarget::desiredGeometry() {
|
||||
return m_group->current()->m_target->desiredGeometry();
|
||||
}
|
||||
|
||||
PHLWINDOW CWindowGroupTarget::window() const {
|
||||
return m_group->current();
|
||||
}
|
||||
|
||||
eFullscreenMode CWindowGroupTarget::fullscreenMode() {
|
||||
return m_group->current()->m_fullscreenState.internal;
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::setFullscreenMode(eFullscreenMode mode) {
|
||||
m_group->current()->m_fullscreenState.internal = mode;
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CWindowGroupTarget::minSize() {
|
||||
return m_group->current()->minSize();
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CWindowGroupTarget::maxSize() {
|
||||
return m_group->current()->maxSize();
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::damageEntire() {
|
||||
g_pHyprRenderer->damageWindow(m_group->current());
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::warpPositionSize() {
|
||||
for (const auto& w : m_group->windows()) {
|
||||
w->m_target->warpPositionSize();
|
||||
}
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::onUpdateSpace() {
|
||||
for (const auto& w : m_group->windows()) {
|
||||
w->m_target->onUpdateSpace();
|
||||
}
|
||||
}
|
||||
39
src/layout/target/WindowGroupTarget.hpp
Normal file
39
src/layout/target/WindowGroupTarget.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "Target.hpp"
|
||||
|
||||
#include "../../desktop/view/Window.hpp"
|
||||
#include "../../desktop/view/Group.hpp"
|
||||
|
||||
namespace Layout {
|
||||
|
||||
class CWindowGroupTarget : public ITarget {
|
||||
public:
|
||||
static SP<CWindowGroupTarget> create(SP<Desktop::View::CGroup> g);
|
||||
virtual ~CWindowGroupTarget() = default;
|
||||
|
||||
virtual eTargetType type();
|
||||
|
||||
virtual void setPositionGlobal(const CBox& box);
|
||||
virtual void assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
virtual PHLWINDOW window() const;
|
||||
|
||||
virtual bool floating();
|
||||
virtual void setFloating(bool x);
|
||||
virtual std::expected<SGeometryRequested, eGeometryFailure> desiredGeometry();
|
||||
virtual eFullscreenMode fullscreenMode();
|
||||
virtual void setFullscreenMode(eFullscreenMode mode);
|
||||
virtual std::optional<Vector2D> minSize();
|
||||
virtual std::optional<Vector2D> maxSize();
|
||||
virtual void damageEntire();
|
||||
virtual void warpPositionSize();
|
||||
virtual void onUpdateSpace();
|
||||
|
||||
private:
|
||||
CWindowGroupTarget(SP<Desktop::View::CGroup> g);
|
||||
|
||||
void updatePos();
|
||||
|
||||
WP<Desktop::View::CGroup> m_group;
|
||||
};
|
||||
};
|
||||
363
src/layout/target/WindowTarget.cpp
Normal file
363
src/layout/target/WindowTarget.cpp
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
#include "WindowTarget.hpp"
|
||||
|
||||
#include "../space/Space.hpp"
|
||||
#include "../algorithm/Algorithm.hpp"
|
||||
|
||||
#include "../../protocols/core/Compositor.hpp"
|
||||
#include "../../config/ConfigManager.hpp"
|
||||
#include "../../helpers/Monitor.hpp"
|
||||
#include "../../xwayland/XSurface.hpp"
|
||||
#include "../../Compositor.hpp"
|
||||
#include "../../render/Renderer.hpp"
|
||||
|
||||
using namespace Layout;
|
||||
|
||||
SP<ITarget> CWindowTarget::create(PHLWINDOW w) {
|
||||
auto target = SP<CWindowTarget>(new CWindowTarget(w));
|
||||
target->m_self = target;
|
||||
return target;
|
||||
}
|
||||
|
||||
CWindowTarget::CWindowTarget(PHLWINDOW w) : m_window(w) {
|
||||
;
|
||||
}
|
||||
|
||||
eTargetType CWindowTarget::type() {
|
||||
return TARGET_TYPE_WINDOW;
|
||||
}
|
||||
|
||||
void CWindowTarget::setPositionGlobal(const CBox& box) {
|
||||
ITarget::setPositionGlobal(box);
|
||||
|
||||
updatePos();
|
||||
}
|
||||
|
||||
void CWindowTarget::updatePos() {
|
||||
|
||||
if (!m_space)
|
||||
return;
|
||||
|
||||
if (fullscreenMode() == FSMODE_FULLSCREEN)
|
||||
return;
|
||||
|
||||
if (floating() && fullscreenMode() != FSMODE_MAXIMIZED) {
|
||||
m_window->m_position = m_box.pos();
|
||||
m_window->m_size = m_box.size();
|
||||
|
||||
*m_window->m_realPosition = m_box.pos();
|
||||
*m_window->m_realSize = m_box.size();
|
||||
|
||||
m_window->sendWindowSize();
|
||||
m_window->updateWindowDecos();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Tiled is more complicated.
|
||||
|
||||
const auto PMONITOR = m_space->workspace()->m_monitor;
|
||||
const auto PWORKSPACE = m_space->workspace();
|
||||
|
||||
// for gaps outer
|
||||
const auto MONITOR_WORKAREA = m_space->workArea();
|
||||
const bool DISPLAYLEFT = STICKS(m_box.x, MONITOR_WORKAREA.x);
|
||||
const bool DISPLAYRIGHT = STICKS(m_box.x + m_box.w, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w);
|
||||
const bool DISPLAYTOP = STICKS(m_box.y, MONITOR_WORKAREA.y);
|
||||
const bool DISPLAYBOTTOM = STICKS(m_box.y + m_box.h, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h);
|
||||
|
||||
// this is used for scrolling, so that the gaps are correct when a window is the full width and has neighbors
|
||||
const bool DISPLAYINVERSELEFT = STICKS(m_box.x, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w);
|
||||
const bool DISPLAYINVERSERIGHT = STICKS(m_box.x + m_box.w, MONITOR_WORKAREA.x);
|
||||
|
||||
// get specific gaps and rules for this workspace,
|
||||
// if user specified them in config
|
||||
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(PWORKSPACE);
|
||||
|
||||
if (!validMapped(m_window)) {
|
||||
if (m_window)
|
||||
g_layoutManager->removeTarget(m_window->layoutTarget());
|
||||
return;
|
||||
}
|
||||
|
||||
if (fullscreenMode() == FSMODE_FULLSCREEN)
|
||||
return;
|
||||
|
||||
g_pHyprRenderer->damageWindow(window());
|
||||
|
||||
static auto PGAPSINDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_in");
|
||||
auto* const PGAPSIN = sc<CCssGapData*>((PGAPSINDATA.ptr())->getData());
|
||||
|
||||
auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN);
|
||||
CBox nodeBox = m_box;
|
||||
nodeBox.round();
|
||||
|
||||
m_window->m_size = nodeBox.size();
|
||||
m_window->m_position = nodeBox.pos();
|
||||
|
||||
m_window->updateWindowDecos();
|
||||
|
||||
auto calcPos = m_window->m_position;
|
||||
auto calcSize = m_window->m_size;
|
||||
|
||||
const static auto REQUESTEDRATIO = CConfigValue<Hyprlang::VEC2>("layout:single_window_aspect_ratio");
|
||||
const static auto REQUESTEDRATIOTOLERANCE = CConfigValue<Hyprlang::FLOAT>("layout:single_window_aspect_ratio_tolerance");
|
||||
|
||||
Vector2D ratioPadding;
|
||||
|
||||
if ((*REQUESTEDRATIO).y != 0 && m_space->algorithm()->tiledTargets() <= 1) {
|
||||
const Vector2D originalSize = MONITOR_WORKAREA.size();
|
||||
|
||||
const double requestedRatio = (*REQUESTEDRATIO).x / (*REQUESTEDRATIO).y;
|
||||
const double originalRatio = originalSize.x / originalSize.y;
|
||||
|
||||
if (requestedRatio > originalRatio) {
|
||||
double padding = originalSize.y - (originalSize.x / requestedRatio);
|
||||
|
||||
if (padding / 2 > (*REQUESTEDRATIOTOLERANCE) * originalSize.y)
|
||||
ratioPadding = Vector2D{0., padding};
|
||||
} else if (requestedRatio < originalRatio) {
|
||||
double padding = originalSize.x - (originalSize.y * requestedRatio);
|
||||
|
||||
if (padding / 2 > (*REQUESTEDRATIOTOLERANCE) * originalSize.x)
|
||||
ratioPadding = Vector2D{padding, 0.};
|
||||
}
|
||||
}
|
||||
|
||||
const auto GAPOFFSETTOPLEFT = Vector2D(sc<double>(DISPLAYLEFT ? 0 : (DISPLAYINVERSELEFT ? 2 * gapsIn.m_left : gapsIn.m_left)), sc<double>(DISPLAYTOP ? 0 : gapsIn.m_top));
|
||||
|
||||
const auto GAPOFFSETBOTTOMRIGHT =
|
||||
Vector2D(sc<double>(DISPLAYRIGHT ? 0 : (DISPLAYINVERSERIGHT ? 2 * gapsIn.m_right : gapsIn.m_right)), sc<double>(DISPLAYBOTTOM ? 0 : gapsIn.m_bottom));
|
||||
|
||||
calcPos = calcPos + GAPOFFSETTOPLEFT + ratioPadding / 2;
|
||||
calcSize = calcSize - GAPOFFSETTOPLEFT - GAPOFFSETBOTTOMRIGHT - ratioPadding;
|
||||
|
||||
if (isPseudo()) {
|
||||
// Calculate pseudo
|
||||
float scale = 1;
|
||||
|
||||
// adjust if doesn't fit
|
||||
if (m_pseudoSize.x > calcSize.x || m_pseudoSize.y > calcSize.y) {
|
||||
if (m_pseudoSize.x > calcSize.x)
|
||||
scale = calcSize.x / m_pseudoSize.x;
|
||||
|
||||
if (m_pseudoSize.y * scale > calcSize.y)
|
||||
scale = calcSize.y / m_pseudoSize.y;
|
||||
|
||||
auto DELTA = calcSize - m_pseudoSize * scale;
|
||||
calcSize = m_pseudoSize * scale;
|
||||
calcPos = calcPos + DELTA / 2.f; // center
|
||||
} else {
|
||||
auto DELTA = calcSize - m_pseudoSize;
|
||||
calcPos = calcPos + DELTA / 2.f; // center
|
||||
calcSize = m_pseudoSize;
|
||||
}
|
||||
}
|
||||
|
||||
const auto RESERVED = m_window->getFullWindowReservedArea();
|
||||
calcPos = calcPos + RESERVED.topLeft;
|
||||
calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight);
|
||||
|
||||
Vector2D availableSpace = calcSize;
|
||||
|
||||
static auto PCLAMP_TILED = CConfigValue<Hyprlang::INT>("misc:size_limits_tiled");
|
||||
|
||||
if (*PCLAMP_TILED) {
|
||||
const auto borderSize = m_window->getRealBorderSize();
|
||||
Vector2D monitorAvailable = MONITOR_WORKAREA.size() - Vector2D{2.0 * borderSize, 2.0 * borderSize};
|
||||
|
||||
Vector2D minSize = m_window->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable);
|
||||
Vector2D maxSize = m_window->isFullscreen() ? Vector2D{INFINITY, INFINITY} :
|
||||
m_window->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable);
|
||||
calcSize = calcSize.clamp(minSize, maxSize);
|
||||
|
||||
calcPos += (availableSpace - calcSize) / 2.0;
|
||||
|
||||
calcPos.x = std::clamp(calcPos.x, MONITOR_WORKAREA.x + borderSize, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w - calcSize.x - borderSize);
|
||||
calcPos.y = std::clamp(calcPos.y, MONITOR_WORKAREA.y + borderSize, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h - calcSize.y - borderSize);
|
||||
}
|
||||
|
||||
if (m_window->onSpecialWorkspace() && !m_window->isFullscreen()) {
|
||||
// if special, we adjust the coords a bit
|
||||
static auto PSCALEFACTOR = CConfigValue<Hyprlang::FLOAT>("dwindle:special_scale_factor");
|
||||
|
||||
CBox wb = {calcPos + (calcSize - calcSize * *PSCALEFACTOR) / 2.f, calcSize * *PSCALEFACTOR};
|
||||
wb.round(); // avoid rounding mess
|
||||
|
||||
*m_window->m_realPosition = wb.pos();
|
||||
*m_window->m_realSize = wb.size();
|
||||
} else {
|
||||
CBox wb = {calcPos, calcSize};
|
||||
wb.round(); // avoid rounding mess
|
||||
|
||||
*m_window->m_realSize = wb.size();
|
||||
*m_window->m_realPosition = wb.pos();
|
||||
}
|
||||
|
||||
m_window->updateWindowDecos();
|
||||
}
|
||||
|
||||
void CWindowTarget::assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint) {
|
||||
if (!space) {
|
||||
ITarget::assignToSpace(space, focalPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
m_window->m_monitor = space->workspace()->m_monitor;
|
||||
m_window->moveToWorkspace(space->workspace());
|
||||
|
||||
// layout and various update fns want the target to already have m_workspace set
|
||||
ITarget::assignToSpace(space, focalPoint);
|
||||
|
||||
m_window->updateToplevel();
|
||||
m_window->updateWindowDecos();
|
||||
}
|
||||
|
||||
bool CWindowTarget::floating() {
|
||||
return m_window->m_isFloating;
|
||||
}
|
||||
|
||||
void CWindowTarget::setFloating(bool x) {
|
||||
if (x == m_window->m_isFloating)
|
||||
return;
|
||||
|
||||
m_window->m_isFloating = x;
|
||||
m_window->m_pinned = false;
|
||||
|
||||
m_window->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FLOATING);
|
||||
}
|
||||
|
||||
Vector2D CWindowTarget::clampSizeForDesired(const Vector2D& size) const {
|
||||
Vector2D newSize = size;
|
||||
if (const auto m = m_window->minSize(); m)
|
||||
newSize = newSize.clamp(*m);
|
||||
if (const auto m = m_window->maxSize(); m)
|
||||
newSize = newSize.clamp(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}, *m);
|
||||
return newSize;
|
||||
}
|
||||
|
||||
std::expected<SGeometryRequested, eGeometryFailure> CWindowTarget::desiredGeometry() {
|
||||
|
||||
SGeometryRequested requested;
|
||||
|
||||
CBox DESIRED_GEOM = g_pXWaylandManager->getGeometryForWindow(m_window.lock());
|
||||
const auto PMONITOR = m_window->m_monitor.lock();
|
||||
|
||||
requested.size = clampSizeForDesired(DESIRED_GEOM.size());
|
||||
|
||||
if (m_window->m_isX11) {
|
||||
Vector2D xy = {DESIRED_GEOM.x, DESIRED_GEOM.y};
|
||||
xy = g_pXWaylandManager->xwaylandToWaylandCoords(xy);
|
||||
requested.pos = xy;
|
||||
}
|
||||
|
||||
const auto STOREDSIZE = m_window->m_ruleApplicator->persistentSize().valueOrDefault() ? g_pConfigManager->getStoredFloatingSize(m_window.lock()) : std::nullopt;
|
||||
|
||||
if (STOREDSIZE)
|
||||
requested.size = clampSizeForDesired(*STOREDSIZE);
|
||||
|
||||
if (!PMONITOR) {
|
||||
Log::logger->log(Log::ERR, "{:m} has an invalid monitor in desiredGeometry!", m_window.lock());
|
||||
return std::unexpected(GEOMETRY_NO_DESIRED);
|
||||
}
|
||||
|
||||
if (DESIRED_GEOM.width <= 2 || DESIRED_GEOM.height <= 2) {
|
||||
const auto SURFACE = m_window->wlSurface()->resource();
|
||||
|
||||
if (SURFACE->m_current.size.x > 5 && SURFACE->m_current.size.y > 5) {
|
||||
// center on mon and call it a day
|
||||
requested.pos.reset();
|
||||
requested.size = clampSizeForDesired(SURFACE->m_current.size);
|
||||
return requested;
|
||||
}
|
||||
|
||||
if (m_window->m_isX11 && m_window->isX11OverrideRedirect()) {
|
||||
// check OR windows, they like their shit
|
||||
const auto SIZE = clampSizeForDesired(m_window->m_xwaylandSurface->m_geometry.w > 0 && m_window->m_xwaylandSurface->m_geometry.h > 0 ?
|
||||
m_window->m_xwaylandSurface->m_geometry.size() :
|
||||
Vector2D{600, 400});
|
||||
|
||||
if (m_window->m_xwaylandSurface->m_geometry.x != 0 && m_window->m_xwaylandSurface->m_geometry.y != 0) {
|
||||
requested.size = SIZE;
|
||||
requested.pos = g_pXWaylandManager->xwaylandToWaylandCoords(m_window->m_xwaylandSurface->m_geometry.pos());
|
||||
return requested;
|
||||
}
|
||||
}
|
||||
|
||||
return std::unexpected(m_window->m_isX11 && m_window->isX11OverrideRedirect() ? GEOMETRY_INVALID_DESIRED : GEOMETRY_NO_DESIRED);
|
||||
}
|
||||
|
||||
// TODO: detect a popup in a more consistent way.
|
||||
if ((DESIRED_GEOM.x == 0 && DESIRED_GEOM.y == 0) || !m_window->m_isX11) {
|
||||
// middle of parent if available
|
||||
if (!m_window->m_isX11) {
|
||||
if (const auto PARENT = m_window->parent(); PARENT) {
|
||||
const auto POS = PARENT->m_realPosition->goal() + PARENT->m_realSize->goal() / 2.F - DESIRED_GEOM.size() / 2.F;
|
||||
requested.pos = POS;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if it is, we respect where it wants to put itself, but apply monitor offset if outside
|
||||
// most of these are popups
|
||||
|
||||
Vector2D pos;
|
||||
|
||||
if (const auto POPENMON = g_pCompositor->getMonitorFromVector(DESIRED_GEOM.middle()); POPENMON->m_id != PMONITOR->m_id)
|
||||
pos = Vector2D(DESIRED_GEOM.x, DESIRED_GEOM.y) - POPENMON->m_position + PMONITOR->m_position;
|
||||
else
|
||||
pos = Vector2D(DESIRED_GEOM.x, DESIRED_GEOM.y);
|
||||
|
||||
requested.pos = pos;
|
||||
}
|
||||
|
||||
if (DESIRED_GEOM.w <= 2 || DESIRED_GEOM.h <= 2)
|
||||
return std::unexpected(GEOMETRY_NO_DESIRED);
|
||||
|
||||
return requested;
|
||||
}
|
||||
|
||||
PHLWINDOW CWindowTarget::window() const {
|
||||
return m_window.lock();
|
||||
}
|
||||
|
||||
eFullscreenMode CWindowTarget::fullscreenMode() {
|
||||
return m_window->m_fullscreenState.internal;
|
||||
}
|
||||
|
||||
void CWindowTarget::setFullscreenMode(eFullscreenMode mode) {
|
||||
if (floating() && m_window->m_fullscreenState.internal == FSMODE_NONE)
|
||||
rememberFloatingSize(m_box.size());
|
||||
|
||||
m_window->m_fullscreenState.internal = mode;
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CWindowTarget::minSize() {
|
||||
return m_window->minSize();
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CWindowTarget::maxSize() {
|
||||
return m_window->maxSize();
|
||||
}
|
||||
|
||||
void CWindowTarget::damageEntire() {
|
||||
g_pHyprRenderer->damageWindow(m_window.lock());
|
||||
}
|
||||
|
||||
void CWindowTarget::warpPositionSize() {
|
||||
m_window->m_realSize->warp();
|
||||
m_window->m_realPosition->warp();
|
||||
m_window->updateWindowDecos();
|
||||
}
|
||||
|
||||
void CWindowTarget::onUpdateSpace() {
|
||||
if (!space())
|
||||
return;
|
||||
|
||||
m_window->m_monitor = space()->workspace()->m_monitor;
|
||||
m_window->moveToWorkspace(space()->workspace());
|
||||
m_window->updateToplevel();
|
||||
m_window->updateWindowDecos();
|
||||
}
|
||||
40
src/layout/target/WindowTarget.hpp
Normal file
40
src/layout/target/WindowTarget.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include "Target.hpp"
|
||||
|
||||
#include "../../desktop/view/Window.hpp"
|
||||
|
||||
namespace Layout {
|
||||
|
||||
class CWindowTarget : public ITarget {
|
||||
public:
|
||||
static SP<ITarget> create(PHLWINDOW w);
|
||||
virtual ~CWindowTarget() = default;
|
||||
|
||||
virtual eTargetType type();
|
||||
|
||||
virtual void setPositionGlobal(const CBox& box);
|
||||
virtual void assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
virtual PHLWINDOW window() const;
|
||||
|
||||
virtual bool floating();
|
||||
virtual void setFloating(bool x);
|
||||
virtual std::expected<SGeometryRequested, eGeometryFailure> desiredGeometry();
|
||||
virtual eFullscreenMode fullscreenMode();
|
||||
virtual void setFullscreenMode(eFullscreenMode mode);
|
||||
virtual std::optional<Vector2D> minSize();
|
||||
virtual std::optional<Vector2D> maxSize();
|
||||
virtual void damageEntire();
|
||||
virtual void warpPositionSize();
|
||||
virtual void onUpdateSpace();
|
||||
|
||||
private:
|
||||
CWindowTarget(PHLWINDOW w);
|
||||
|
||||
Vector2D clampSizeForDesired(const Vector2D& size) const;
|
||||
|
||||
void updatePos();
|
||||
|
||||
PHLWINDOWREF m_window;
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue