mirror of
https://github.com/hyprwm/hyprland-plugins.git
synced 2025-05-12 13:20:36 +01:00
hyprscrolling: init plugin
This commit is contained in:
parent
16fddaab1e
commit
6e215e4f9d
10 changed files with 844 additions and 0 deletions
|
@ -12,6 +12,7 @@ This repo houses official plugins for Hyprland.
|
|||
- csgo-vulkan-fix -> fixes custom resolutions on CS:GO with `-vulkan`
|
||||
- hyprbars -> adds title bars to windows
|
||||
- hyprexpo -> adds an expo-like workspace overview
|
||||
- hyprscrolling -> adds a scrolling layout to hyprland
|
||||
- hyprtrails -> adds smooth trails behind moving windows
|
||||
- hyprwinwrap -> clone of xwinwrap, allows you to put any app as a wallpaper
|
||||
- xtra-dispatchers -> adds some new dispatchers
|
||||
|
|
27
hyprscrolling/CMakeLists.txt
Normal file
27
hyprscrolling/CMakeLists.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
cmake_minimum_required(VERSION 3.27)
|
||||
|
||||
project(hyprscrolling
|
||||
DESCRIPTION "hyprscrolling plugin for Hyprland"
|
||||
VERSION 0.1
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
|
||||
file(GLOB_RECURSE SRC "*.cpp")
|
||||
|
||||
add_library(hyprscrolling SHARED ${SRC})
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET
|
||||
hyprland
|
||||
libdrm
|
||||
libinput
|
||||
libudev
|
||||
pangocairo
|
||||
pixman-1
|
||||
wayland-server
|
||||
xkbcommon
|
||||
)
|
||||
target_link_libraries(hyprscrolling PRIVATE rt PkgConfig::deps)
|
||||
|
||||
install(TARGETS hyprscrolling)
|
4
hyprscrolling/Makefile
Normal file
4
hyprscrolling/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
all:
|
||||
$(CXX) -shared -fPIC --no-gnu-unique main.cpp Scrolling.cpp -o hyprscrolling.so -g `pkg-config --cflags pixman-1 libdrm hyprland pangocairo libinput libudev wayland-server xkbcommon` -std=c++2b
|
||||
clean:
|
||||
rm ./hyprscrolling.so
|
23
hyprscrolling/README.md
Normal file
23
hyprscrolling/README.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
# hyprscrolling
|
||||
|
||||
Adds a scrolling layout to Hyprland.
|
||||
|
||||
**This plugin is a work in progress!**
|
||||
|
||||
## Config
|
||||
|
||||
*All config values are in `plugin:hyprscrolling`.*
|
||||
|
||||
| name | description | type | default |
|
||||
| -- | -- | -- | -- |
|
||||
| fullscreen_on_one_column | if there's only one column, should it be fullscreen | bool | false |
|
||||
| column_width | default column width as a fraction of the monitor width | float [0 - 1] | 0.5 |
|
||||
|
||||
|
||||
## Layout messages
|
||||
|
||||
| name | description | params |
|
||||
| --- | --- | --- |
|
||||
| move | move the layout horizontally, by either a relative logical px (`-200`, `+200`) or columns (`+col`, `-col`) | move data |
|
||||
| colresize | resize the current column, to either a value or by a relative value e.g. `0.5`, `+0.2`, `-0.2` | relative float |
|
||||
| movewindowto | same as the movewindow dispatcher but supports promotion to the right at the end | direction |
|
575
hyprscrolling/Scrolling.cpp
Normal file
575
hyprscrolling/Scrolling.cpp
Normal file
|
@ -0,0 +1,575 @@
|
|||
#include "Scrolling.hpp"
|
||||
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/config/ConfigManager.hpp>
|
||||
#include <hyprland/src/config/ConfigValue.hpp>
|
||||
#include <hyprland/src/render/Renderer.hpp>
|
||||
|
||||
void SColumnData::add(PHLWINDOW w) {
|
||||
windowDatas.emplace_back(makeShared<SScrollingWindowData>(w, self.lock()));
|
||||
}
|
||||
|
||||
void SColumnData::add(SP<SScrollingWindowData> w) {
|
||||
windowDatas.emplace_back(w);
|
||||
w->column = self;
|
||||
}
|
||||
|
||||
void SColumnData::remove(PHLWINDOW w) {
|
||||
std::erase_if(windowDatas, [&w](const auto& e) { return e->window == w; });
|
||||
if (windowDatas.empty() && workspace)
|
||||
workspace->remove(self.lock());
|
||||
}
|
||||
|
||||
void SColumnData::up(SP<SScrollingWindowData> w) {
|
||||
for (size_t i = 1; i < windowDatas.size(); ++i) {
|
||||
if (windowDatas[i] != w)
|
||||
continue;
|
||||
|
||||
std::swap(windowDatas[i], windowDatas[i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
void SColumnData::down(SP<SScrollingWindowData> w) {
|
||||
for (size_t i = 0; i < windowDatas.size() - 1; ++i) {
|
||||
if (windowDatas[i] != w)
|
||||
continue;
|
||||
|
||||
std::swap(windowDatas[i], windowDatas[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
SP<SColumnData> SWorkspaceData::add() {
|
||||
static const auto PCOLWIDTH = CConfigValue<Hyprlang::FLOAT>("plugin:hyprscrolling:column_width");
|
||||
auto col = columns.emplace_back(makeShared<SColumnData>(self.lock()));
|
||||
col->self = col;
|
||||
col->columnWidth = *PCOLWIDTH;
|
||||
return col;
|
||||
}
|
||||
|
||||
void SWorkspaceData::remove(SP<SColumnData> c) {
|
||||
std::erase(columns, c);
|
||||
}
|
||||
|
||||
SP<SColumnData> SWorkspaceData::next(SP<SColumnData> c) {
|
||||
for (size_t i = 0; i < columns.size(); ++i) {
|
||||
if (columns[i] != c)
|
||||
continue;
|
||||
|
||||
if (i == columns.size() - 1)
|
||||
return nullptr;
|
||||
|
||||
return columns[i + 1];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SP<SColumnData> SWorkspaceData::prev(SP<SColumnData> c) {
|
||||
for (size_t i = 0; i < columns.size(); ++i) {
|
||||
if (columns[i] != c)
|
||||
continue;
|
||||
|
||||
if (i == 0)
|
||||
return nullptr;
|
||||
|
||||
return columns[i - 1];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SWorkspaceData::centerCol(SP<SColumnData> c) {
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
static const auto PFSONONE = CConfigValue<Hyprlang::INT>("plugin:hyprscrolling:fullscreen_on_one_column");
|
||||
|
||||
PHLMONITOR PMONITOR = workspace->m_monitor.lock();
|
||||
double currentLeft = 0;
|
||||
|
||||
for (const auto& COL : columns) {
|
||||
const double ITEM_WIDTH = *PFSONONE && columns.size() == 1 ? PMONITOR->m_size.x : PMONITOR->m_size.x * COL->columnWidth;
|
||||
|
||||
if (COL != c)
|
||||
currentLeft += ITEM_WIDTH;
|
||||
else {
|
||||
leftOffset = -currentLeft + ITEM_WIDTH / 2.0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SP<SColumnData> SWorkspaceData::atCenter() {
|
||||
static const auto PFSONONE = CConfigValue<Hyprlang::INT>("plugin:hyprscrolling:fullscreen_on_one_column");
|
||||
|
||||
PHLMONITOR PMONITOR = workspace->m_monitor.lock();
|
||||
double currentLeft = leftOffset;
|
||||
|
||||
for (const auto& COL : columns) {
|
||||
const double ITEM_WIDTH = *PFSONONE && columns.size() == 1 ? PMONITOR->m_size.x : PMONITOR->m_size.x * COL->columnWidth;
|
||||
|
||||
currentLeft += ITEM_WIDTH;
|
||||
|
||||
if (currentLeft >= PMONITOR->m_size.x / 2.0 - 2)
|
||||
return COL;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SWorkspaceData::recalculate() {
|
||||
static const auto PFSONONE = CConfigValue<Hyprlang::INT>("plugin:hyprscrolling:fullscreen_on_one_column");
|
||||
|
||||
if (!workspace || !workspace) {
|
||||
Debug::log(ERR, "[scroller] broken internal state on workspace data");
|
||||
return;
|
||||
}
|
||||
|
||||
leftOffset = std::clamp((double)leftOffset, -maxWidth(), 0.0);
|
||||
|
||||
const auto MAX_WIDTH = maxWidth();
|
||||
|
||||
PHLMONITOR PMONITOR = workspace->m_monitor.lock();
|
||||
double currentLeft = MAX_WIDTH < PMONITOR->m_size.x ? std::round((PMONITOR->m_size.x - MAX_WIDTH) / 2.0) : leftOffset; // layout pixels
|
||||
|
||||
for (const auto& COL : columns) {
|
||||
double currentTop = 0.0;
|
||||
const double ITEM_HEIGHT = PMONITOR->m_size.y / COL->windowDatas.size();
|
||||
const double ITEM_WIDTH = *PFSONONE && columns.size() == 1 ? PMONITOR->m_size.x : PMONITOR->m_size.x * COL->columnWidth;
|
||||
|
||||
for (const auto& WINDOW : COL->windowDatas) {
|
||||
WINDOW->layoutBox = CBox{currentLeft, currentTop, ITEM_WIDTH, ITEM_HEIGHT}.translate(PMONITOR->m_position);
|
||||
|
||||
currentTop += ITEM_HEIGHT;
|
||||
|
||||
layout->applyNodeDataToWindow(WINDOW, false);
|
||||
}
|
||||
|
||||
currentLeft += ITEM_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
double SWorkspaceData::maxWidth() {
|
||||
static const auto PFSONONE = CConfigValue<Hyprlang::INT>("plugin:hyprscrolling:fullscreen_on_one_column");
|
||||
|
||||
PHLMONITOR PMONITOR = workspace->m_monitor.lock();
|
||||
double currentLeft = 0;
|
||||
|
||||
for (const auto& COL : columns) {
|
||||
const double ITEM_WIDTH = *PFSONONE && columns.size() == 1 ? PMONITOR->m_size.x : PMONITOR->m_size.x * COL->columnWidth;
|
||||
|
||||
currentLeft += ITEM_WIDTH;
|
||||
}
|
||||
|
||||
return currentLeft;
|
||||
}
|
||||
|
||||
void CScrollingLayout::applyNodeDataToWindow(SP<SScrollingWindowData> data, bool force) {
|
||||
if (!data || !data->column || !data->column->workspace) {
|
||||
Debug::log(ERR, "[scroller] broken internal state on node");
|
||||
return;
|
||||
}
|
||||
|
||||
PHLMONITOR PMONITOR = data->column->workspace->workspace ? data->column->workspace->workspace->m_monitor.lock() : nullptr;
|
||||
|
||||
if (!PMONITOR) {
|
||||
Debug::log(ERR, "[scroller] broken internal state on workspace");
|
||||
return;
|
||||
}
|
||||
|
||||
// for gaps outer
|
||||
const bool DISPLAYLEFT = STICKS(data->layoutBox.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x);
|
||||
const bool DISPLAYRIGHT = STICKS(data->layoutBox.x + data->layoutBox.w, PMONITOR->m_position.x + PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x);
|
||||
const bool DISPLAYTOP = STICKS(data->layoutBox.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y);
|
||||
const bool DISPLAYBOTTOM = STICKS(data->layoutBox.y + data->layoutBox.h, PMONITOR->m_position.y + PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y);
|
||||
|
||||
const auto PWINDOW = data->window.lock();
|
||||
// get specific gaps and rules for this workspace,
|
||||
// if user specified them in config
|
||||
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(g_pCompositor->getWorkspaceByID(data->column->workspace->workspace->m_id));
|
||||
|
||||
if (!validMapped(PWINDOW)) {
|
||||
Debug::log(ERR, "Node {} holding invalid {}!!", (uintptr_t)data.get(), PWINDOW);
|
||||
onWindowRemovedTiling(PWINDOW);
|
||||
return;
|
||||
}
|
||||
|
||||
if (PWINDOW->isFullscreen())
|
||||
return;
|
||||
|
||||
PWINDOW->unsetWindowData(PRIORITY_LAYOUT);
|
||||
PWINDOW->updateWindowData();
|
||||
|
||||
static auto PGAPSINDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_in");
|
||||
static auto PGAPSOUTDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_out");
|
||||
auto* const PGAPSIN = (CCssGapData*)(PGAPSINDATA.ptr())->getData();
|
||||
auto* const PGAPSOUT = (CCssGapData*)(PGAPSOUTDATA.ptr())->getData();
|
||||
|
||||
auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN);
|
||||
auto gapsOut = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT);
|
||||
CBox nodeBox = data->layoutBox;
|
||||
nodeBox.round();
|
||||
|
||||
PWINDOW->m_size = nodeBox.size();
|
||||
PWINDOW->m_position = nodeBox.pos();
|
||||
|
||||
PWINDOW->updateWindowDecos();
|
||||
|
||||
auto calcPos = PWINDOW->m_position;
|
||||
auto calcSize = PWINDOW->m_size;
|
||||
|
||||
const auto OFFSETTOPLEFT = Vector2D((double)(DISPLAYLEFT ? gapsOut.m_left : gapsIn.m_left), (double)(DISPLAYTOP ? gapsOut.m_top : gapsIn.m_top));
|
||||
|
||||
const auto OFFSETBOTTOMRIGHT = Vector2D((double)(DISPLAYRIGHT ? gapsOut.m_right : gapsIn.m_right), (double)(DISPLAYBOTTOM ? gapsOut.m_bottom : gapsIn.m_bottom));
|
||||
|
||||
calcPos = calcPos + OFFSETTOPLEFT;
|
||||
calcSize = calcSize - OFFSETTOPLEFT - OFFSETBOTTOMRIGHT;
|
||||
|
||||
if (PWINDOW->m_isPseudotiled) {
|
||||
// Calculate pseudo
|
||||
float scale = 1;
|
||||
|
||||
// adjust if doesnt fit
|
||||
if (PWINDOW->m_pseudoSize.x > calcSize.x || PWINDOW->m_pseudoSize.y > calcSize.y) {
|
||||
if (PWINDOW->m_pseudoSize.x > calcSize.x) {
|
||||
scale = calcSize.x / PWINDOW->m_pseudoSize.x;
|
||||
}
|
||||
|
||||
if (PWINDOW->m_pseudoSize.y * scale > calcSize.y) {
|
||||
scale = calcSize.y / PWINDOW->m_pseudoSize.y;
|
||||
}
|
||||
|
||||
auto DELTA = calcSize - PWINDOW->m_pseudoSize * scale;
|
||||
calcSize = PWINDOW->m_pseudoSize * scale;
|
||||
calcPos = calcPos + DELTA / 2.f; // center
|
||||
} else {
|
||||
auto DELTA = calcSize - PWINDOW->m_pseudoSize;
|
||||
calcPos = calcPos + DELTA / 2.f; // center
|
||||
calcSize = PWINDOW->m_pseudoSize;
|
||||
}
|
||||
}
|
||||
|
||||
const auto RESERVED = PWINDOW->getFullWindowReservedArea();
|
||||
calcPos = calcPos + RESERVED.topLeft;
|
||||
calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight);
|
||||
|
||||
if (PWINDOW->onSpecialWorkspace() && !PWINDOW->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
|
||||
|
||||
*PWINDOW->m_realPosition = wb.pos();
|
||||
*PWINDOW->m_realSize = wb.size();
|
||||
} else {
|
||||
CBox wb = {calcPos, calcSize};
|
||||
wb.round(); // avoid rounding mess
|
||||
|
||||
*PWINDOW->m_realSize = wb.size();
|
||||
*PWINDOW->m_realPosition = wb.pos();
|
||||
}
|
||||
|
||||
if (force) {
|
||||
g_pHyprRenderer->damageWindow(PWINDOW);
|
||||
|
||||
PWINDOW->m_realPosition->warp();
|
||||
PWINDOW->m_realSize->warp();
|
||||
|
||||
g_pHyprRenderer->damageWindow(PWINDOW);
|
||||
}
|
||||
|
||||
PWINDOW->updateWindowDecos();
|
||||
}
|
||||
|
||||
void CScrollingLayout::onEnable() {
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (w->m_isFloating || !w->m_isMapped || w->isHidden())
|
||||
continue;
|
||||
|
||||
onWindowCreatedTiling(w);
|
||||
}
|
||||
}
|
||||
|
||||
void CScrollingLayout::onDisable() {
|
||||
m_workspaceDatas.clear();
|
||||
}
|
||||
|
||||
void CScrollingLayout::onWindowCreatedTiling(PHLWINDOW window, eDirection direction) {
|
||||
auto workspaceData = dataFor(window->m_workspace);
|
||||
|
||||
if (!workspaceData) {
|
||||
Debug::log(LOG, "[scrolling] No workspace data yet, creating");
|
||||
workspaceData = m_workspaceDatas.emplace_back(makeShared<SWorkspaceData>(window->m_workspace, this));
|
||||
workspaceData->self = workspaceData;
|
||||
}
|
||||
|
||||
const auto PLASTFOCUS = g_pCompositor->m_lastWindow.lock();
|
||||
const SP<SScrollingWindowData> LAST_FOCUS_DATA = PLASTFOCUS ? dataFor(PLASTFOCUS) : nullptr;
|
||||
const auto PMONITOR = window->m_monitor;
|
||||
|
||||
bool addNewColumn = !PLASTFOCUS || !LAST_FOCUS_DATA || PLASTFOCUS->m_workspace != window->m_workspace || workspaceData->columns.size() <= 1;
|
||||
|
||||
if (!addNewColumn && PMONITOR) {
|
||||
if (workspaceData->atCenter() == nullptr)
|
||||
addNewColumn = true;
|
||||
}
|
||||
|
||||
Debug::log(LOG, "[scrolling] new window {:x}, addNewColumn: {}, columns before: {}", (uintptr_t)window.get(), addNewColumn, workspaceData->columns.size());
|
||||
|
||||
if (addNewColumn) {
|
||||
auto col = workspaceData->add();
|
||||
col->add(window);
|
||||
} else {
|
||||
// LAST_FOCUS_DATA has to be valid
|
||||
auto col = LAST_FOCUS_DATA->column;
|
||||
col->add(window);
|
||||
}
|
||||
|
||||
workspaceData->recalculate();
|
||||
}
|
||||
|
||||
void CScrollingLayout::onWindowRemovedTiling(PHLWINDOW window) {
|
||||
const auto DATA = dataFor(window);
|
||||
|
||||
if (!DATA)
|
||||
return;
|
||||
|
||||
DATA->column->remove(window);
|
||||
}
|
||||
|
||||
bool CScrollingLayout::isWindowTiled(PHLWINDOW window) {
|
||||
const auto DATA = dataFor(window);
|
||||
|
||||
return DATA;
|
||||
}
|
||||
|
||||
void CScrollingLayout::recalculateMonitor(const MONITORID& id) {
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(id);
|
||||
if (!PMONITOR || !PMONITOR->m_activeWorkspace)
|
||||
return;
|
||||
|
||||
const auto DATA = dataFor(PMONITOR->m_activeWorkspace);
|
||||
|
||||
if (!DATA)
|
||||
return;
|
||||
|
||||
DATA->recalculate();
|
||||
}
|
||||
|
||||
void CScrollingLayout::recalculateWindow(PHLWINDOW window) {
|
||||
if (!window->m_workspace)
|
||||
return;
|
||||
|
||||
const auto DATA = dataFor(window->m_workspace);
|
||||
|
||||
if (!DATA)
|
||||
return;
|
||||
|
||||
DATA->recalculate();
|
||||
}
|
||||
|
||||
void CScrollingLayout::onBeginDragWindow() {
|
||||
IHyprLayout::onBeginDragWindow();
|
||||
}
|
||||
|
||||
void CScrollingLayout::resizeActiveWindow(const Vector2D&, eRectCorner corner, PHLWINDOW pWindow) {
|
||||
;
|
||||
}
|
||||
|
||||
void CScrollingLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE) {
|
||||
;
|
||||
}
|
||||
|
||||
std::any CScrollingLayout::layoutMessage(SLayoutMessageHeader header, std::string message) {
|
||||
const auto ARGS = CVarList(message, 0, ' ');
|
||||
if (ARGS[0] == "move") {
|
||||
const auto DATA = currentWorkspaceData();
|
||||
if (!DATA)
|
||||
return {};
|
||||
|
||||
if (ARGS[1] == "+col" || ARGS[1] == "col") {
|
||||
const auto WDATA = dataFor(g_pCompositor->m_lastWindow.lock());
|
||||
if (!WDATA)
|
||||
return {};
|
||||
|
||||
const auto COL = DATA->next(WDATA->column.lock());
|
||||
if (!COL) {
|
||||
// move to max
|
||||
DATA->leftOffset = -DATA->maxWidth();
|
||||
DATA->recalculate();
|
||||
g_pCompositor->focusWindow(nullptr);
|
||||
return {};
|
||||
}
|
||||
|
||||
DATA->centerCol(COL);
|
||||
DATA->recalculate();
|
||||
|
||||
g_pCompositor->focusWindow(COL->windowDatas.front()->window.lock());
|
||||
|
||||
return {};
|
||||
} else if (ARGS[1] == "-col") {
|
||||
const auto WDATA = dataFor(g_pCompositor->m_lastWindow.lock());
|
||||
if (!WDATA) {
|
||||
if (DATA->leftOffset <= DATA->maxWidth() && DATA->columns.size() > 0) {
|
||||
DATA->centerCol(DATA->columns.back());
|
||||
DATA->recalculate();
|
||||
g_pCompositor->focusWindow((DATA->columns.back()->windowDatas.back())->window.lock());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto COL = DATA->prev(WDATA->column.lock());
|
||||
if (!COL)
|
||||
return {};
|
||||
DATA->centerCol(COL);
|
||||
DATA->recalculate();
|
||||
|
||||
g_pCompositor->focusWindow(COL->windowDatas.back()->window.lock());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto PLUSMINUS = getPlusMinusKeywordResult(ARGS[1], 0);
|
||||
|
||||
if (!PLUSMINUS.has_value())
|
||||
return {};
|
||||
|
||||
DATA->leftOffset -= *PLUSMINUS;
|
||||
DATA->recalculate();
|
||||
|
||||
const auto ATCENTER = DATA->atCenter();
|
||||
|
||||
g_pCompositor->focusWindow(ATCENTER ? (*ATCENTER->windowDatas.begin())->window.lock() : nullptr);
|
||||
} else if (ARGS[0] == "colresize") {
|
||||
const auto WDATA = dataFor(g_pCompositor->m_lastWindow.lock());
|
||||
|
||||
if (!WDATA)
|
||||
return {};
|
||||
|
||||
if (ARGS[1][0] == '+' || ARGS[1][0] == '-') {
|
||||
const auto PLUSMINUS = getPlusMinusKeywordResult(ARGS[1], 0);
|
||||
|
||||
if (!PLUSMINUS.has_value())
|
||||
return {};
|
||||
|
||||
WDATA->column->columnWidth += *PLUSMINUS;
|
||||
} else {
|
||||
float abs = 0;
|
||||
try {
|
||||
abs = std::stof(ARGS[1]);
|
||||
} catch (...) { return {}; }
|
||||
|
||||
WDATA->column->columnWidth = abs;
|
||||
}
|
||||
|
||||
WDATA->column->columnWidth = std::clamp(WDATA->column->columnWidth, 0.05F, 1.F);
|
||||
|
||||
WDATA->column->workspace->recalculate();
|
||||
} else if (ARGS[0] == "movewindowto") {
|
||||
moveWindowTo(g_pCompositor->m_lastWindow.lock(), ARGS[1], false);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
SWindowRenderLayoutHints CScrollingLayout::requestRenderHints(PHLWINDOW a) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void CScrollingLayout::switchWindows(PHLWINDOW a, PHLWINDOW b) {
|
||||
;
|
||||
}
|
||||
|
||||
void CScrollingLayout::moveWindowTo(PHLWINDOW w, const std::string& dir, bool silent) {
|
||||
const auto DATA = dataFor(w);
|
||||
|
||||
if (!DATA)
|
||||
return;
|
||||
|
||||
const auto WS = DATA->column->workspace.lock();
|
||||
|
||||
if (dir == "l") {
|
||||
const auto COL = WS->prev(DATA->column.lock());
|
||||
if (!COL)
|
||||
return;
|
||||
|
||||
DATA->column->remove(w);
|
||||
COL->add(DATA);
|
||||
} else if (dir == "r") {
|
||||
const auto COL = WS->next(DATA->column.lock());
|
||||
|
||||
DATA->column->remove(w);
|
||||
|
||||
if (!COL) {
|
||||
// make a new one
|
||||
const auto NEWCOL = WS->add();
|
||||
NEWCOL->add(DATA);
|
||||
} else
|
||||
COL->add(DATA);
|
||||
|
||||
} else if (dir == "t" || dir == "u")
|
||||
DATA->column->up(DATA);
|
||||
else if (dir == "b" || dir == "d")
|
||||
DATA->column->down(DATA);
|
||||
|
||||
WS->recalculate();
|
||||
}
|
||||
|
||||
void CScrollingLayout::alterSplitRatio(PHLWINDOW, float, bool) {
|
||||
;
|
||||
}
|
||||
|
||||
std::string CScrollingLayout::getLayoutName() {
|
||||
return "scrolling";
|
||||
}
|
||||
|
||||
void CScrollingLayout::replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to) {
|
||||
;
|
||||
}
|
||||
|
||||
Vector2D CScrollingLayout::predictSizeForNewWindowTiled() {
|
||||
return Vector2D{};
|
||||
}
|
||||
|
||||
SP<SWorkspaceData> CScrollingLayout::dataFor(PHLWORKSPACE ws) {
|
||||
for (const auto& e : m_workspaceDatas) {
|
||||
if (e->workspace != ws)
|
||||
continue;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SP<SScrollingWindowData> CScrollingLayout::dataFor(PHLWINDOW w) {
|
||||
if (!w)
|
||||
return nullptr;
|
||||
|
||||
for (const auto& e : m_workspaceDatas) {
|
||||
if (e->workspace != w->m_workspace)
|
||||
continue;
|
||||
|
||||
for (const auto& c : e->columns) {
|
||||
for (const auto& d : c->windowDatas) {
|
||||
if (d->window != w)
|
||||
continue;
|
||||
|
||||
return d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SP<SWorkspaceData> CScrollingLayout::currentWorkspaceData() {
|
||||
if (!g_pCompositor->m_lastMonitor || !g_pCompositor->m_lastMonitor->m_activeWorkspace)
|
||||
return nullptr;
|
||||
|
||||
// FIXME: special
|
||||
|
||||
return dataFor(g_pCompositor->m_lastMonitor->m_activeWorkspace);
|
||||
}
|
98
hyprscrolling/Scrolling.hpp
Normal file
98
hyprscrolling/Scrolling.hpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <hyprland/src/layout/IHyprLayout.hpp>
|
||||
#include <hyprland/src/helpers/memory/Memory.hpp>
|
||||
|
||||
class CScrollingLayout;
|
||||
struct SColumnData;
|
||||
struct SWorkspaceData;
|
||||
|
||||
struct SScrollingWindowData {
|
||||
SScrollingWindowData(PHLWINDOW w, SP<SColumnData> col) : window(w), column(col) {
|
||||
;
|
||||
}
|
||||
|
||||
PHLWINDOWREF window;
|
||||
WP<SColumnData> column;
|
||||
|
||||
CBox layoutBox;
|
||||
};
|
||||
|
||||
struct SColumnData {
|
||||
SColumnData(SP<SWorkspaceData> ws) : workspace(ws) {
|
||||
;
|
||||
}
|
||||
|
||||
void add(PHLWINDOW w);
|
||||
void add(SP<SScrollingWindowData> w);
|
||||
void remove(PHLWINDOW w);
|
||||
|
||||
void up(SP<SScrollingWindowData> w);
|
||||
void down(SP<SScrollingWindowData> w);
|
||||
|
||||
std::vector<SP<SScrollingWindowData>> windowDatas;
|
||||
float columnSize = 1.F;
|
||||
float columnWidth = 1.F;
|
||||
WP<SWorkspaceData> workspace;
|
||||
|
||||
WP<SColumnData> self;
|
||||
};
|
||||
|
||||
struct SWorkspaceData {
|
||||
SWorkspaceData(PHLWORKSPACE w, CScrollingLayout* l) : workspace(w), layout(l) {
|
||||
;
|
||||
}
|
||||
|
||||
PHLWORKSPACEREF workspace;
|
||||
std::vector<SP<SColumnData>> columns;
|
||||
int leftOffset = 0;
|
||||
|
||||
SP<SColumnData> add();
|
||||
void remove(SP<SColumnData> c);
|
||||
double maxWidth();
|
||||
SP<SColumnData> next(SP<SColumnData> c);
|
||||
SP<SColumnData> prev(SP<SColumnData> c);
|
||||
SP<SColumnData> atCenter();
|
||||
|
||||
void centerCol(SP<SColumnData> c);
|
||||
|
||||
void recalculate();
|
||||
|
||||
CScrollingLayout* layout = nullptr;
|
||||
WP<SWorkspaceData> self;
|
||||
};
|
||||
|
||||
class CScrollingLayout : public IHyprLayout {
|
||||
public:
|
||||
virtual void onWindowCreatedTiling(PHLWINDOW, eDirection direction = DIRECTION_DEFAULT);
|
||||
virtual void onWindowRemovedTiling(PHLWINDOW);
|
||||
virtual bool isWindowTiled(PHLWINDOW);
|
||||
virtual void recalculateMonitor(const MONITORID&);
|
||||
virtual void recalculateWindow(PHLWINDOW);
|
||||
virtual void onBeginDragWindow();
|
||||
virtual void resizeActiveWindow(const Vector2D&, eRectCorner corner = CORNER_NONE, PHLWINDOW pWindow = nullptr);
|
||||
virtual void fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE);
|
||||
virtual std::any layoutMessage(SLayoutMessageHeader, std::string);
|
||||
virtual SWindowRenderLayoutHints requestRenderHints(PHLWINDOW);
|
||||
virtual void switchWindows(PHLWINDOW, PHLWINDOW);
|
||||
virtual void moveWindowTo(PHLWINDOW, const std::string& dir, bool silent);
|
||||
virtual void alterSplitRatio(PHLWINDOW, float, bool);
|
||||
virtual std::string getLayoutName();
|
||||
virtual void replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to);
|
||||
virtual Vector2D predictSizeForNewWindowTiled();
|
||||
|
||||
virtual void onEnable();
|
||||
virtual void onDisable();
|
||||
|
||||
private:
|
||||
std::vector<SP<SWorkspaceData>> m_workspaceDatas;
|
||||
|
||||
SP<SWorkspaceData> dataFor(PHLWORKSPACE ws);
|
||||
SP<SScrollingWindowData> dataFor(PHLWINDOW w);
|
||||
SP<SWorkspaceData> currentWorkspaceData();
|
||||
|
||||
void applyNodeDataToWindow(SP<SScrollingWindowData> node, bool force);
|
||||
|
||||
friend struct SWorkspaceData;
|
||||
};
|
19
hyprscrolling/default.nix
Normal file
19
hyprscrolling/default.nix
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
lib,
|
||||
hyprland,
|
||||
hyprlandPlugins,
|
||||
}:
|
||||
hyprlandPlugins.mkHyprlandPlugin hyprland {
|
||||
pluginName = "xtra-dispatchers";
|
||||
version = "0.1";
|
||||
src = ./.;
|
||||
|
||||
inherit (hyprland) nativeBuildInputs;
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://github.com/hyprwm/hyprland-plugins";
|
||||
description = "Hyprland extra dispatchers plugin";
|
||||
license = licenses.bsd3;
|
||||
platforms = platforms.linux;
|
||||
};
|
||||
}
|
5
hyprscrolling/globals.hpp
Normal file
5
hyprscrolling/globals.hpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <hyprland/src/plugins/PluginAPI.hpp>
|
||||
|
||||
inline HANDLE PHANDLE = nullptr;
|
62
hyprscrolling/main.cpp
Normal file
62
hyprscrolling/main.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
#define WLR_USE_UNSTABLE
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <hyprland/src/includes.hpp>
|
||||
#include <sstream>
|
||||
|
||||
#define private public
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/desktop/Window.hpp>
|
||||
#include <hyprland/src/config/ConfigManager.hpp>
|
||||
#include <hyprland/src/render/Renderer.hpp>
|
||||
#include <hyprland/src/managers/KeybindManager.hpp>
|
||||
#undef private
|
||||
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
#include "globals.hpp"
|
||||
#include "Scrolling.hpp"
|
||||
|
||||
// Do NOT change this function.
|
||||
APICALL EXPORT std::string PLUGIN_API_VERSION() {
|
||||
return HYPRLAND_API_VERSION;
|
||||
}
|
||||
|
||||
UP<CScrollingLayout> g_pScrollingLayout;
|
||||
|
||||
//
|
||||
|
||||
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||
PHANDLE = handle;
|
||||
|
||||
const std::string HASH = __hyprland_api_get_hash();
|
||||
|
||||
if (HASH != GIT_COMMIT_HASH) {
|
||||
HyprlandAPI::addNotification(PHANDLE, "[hyprscrolling] Failure in initialization: Version mismatch (headers ver is not equal to running hyprland ver)",
|
||||
CHyprColor{1.0, 0.2, 0.2, 1.0}, 5000);
|
||||
throw std::runtime_error("[hs] Version mismatch");
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
|
||||
g_pScrollingLayout = makeUnique<CScrollingLayout>();
|
||||
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprscrolling:fullscreen_on_one_column", Hyprlang::INT{0});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprscrolling:column_width", Hyprlang::FLOAT{0.5F});
|
||||
HyprlandAPI::addLayout(PHANDLE, "scrolling", g_pScrollingLayout.get());
|
||||
|
||||
if (success) HyprlandAPI::addNotification(PHANDLE, "[hyprscrolling] Initialized successfully!", CHyprColor{0.2, 1.0, 0.2, 1.0}, 5000);
|
||||
else {
|
||||
HyprlandAPI::addNotification(PHANDLE, "[hyprscrolling] Failure in initialization: failed to register dispatchers", CHyprColor{1.0, 0.2, 0.2, 1.0}, 5000);
|
||||
throw std::runtime_error("[hs] Dispatchers failed");
|
||||
}
|
||||
|
||||
return {"hyprscrolling", "A plugin to add a scrolling layout to hyprland", "Vaxry", "1.0"};
|
||||
}
|
||||
|
||||
APICALL EXPORT void PLUGIN_EXIT() {
|
||||
HyprlandAPI::removeLayout(PHANDLE, g_pScrollingLayout.get());
|
||||
g_pScrollingLayout.reset();
|
||||
}
|
30
hyprscrolling/meson.build
Normal file
30
hyprscrolling/meson.build
Normal file
|
@ -0,0 +1,30 @@
|
|||
project('hyprscrolling', 'cpp',
|
||||
version: '0.1',
|
||||
default_options: ['buildtype=release'],
|
||||
)
|
||||
|
||||
cpp_compiler = meson.get_compiler('cpp')
|
||||
if cpp_compiler.has_argument('-std=c++23')
|
||||
add_global_arguments('-std=c++23', language: 'cpp')
|
||||
elif cpp_compiler.has_argument('-std=c++2b')
|
||||
add_global_arguments('-std=c++2b', language: 'cpp')
|
||||
else
|
||||
error('Could not configure current C++ compiler (' + cpp_compiler.get_id() + ' ' + cpp_compiler.version() + ') with required C++ standard (C++23)')
|
||||
endif
|
||||
|
||||
globber = run_command('find', '.', '-name', '*.cpp', check: true)
|
||||
src = globber.stdout().strip().split('\n')
|
||||
|
||||
shared_module(meson.project_name(), src,
|
||||
dependencies: [
|
||||
dependency('hyprland'),
|
||||
dependency('pixman-1'),
|
||||
dependency('libdrm'),
|
||||
dependency('pangocairo'),
|
||||
dependency('libinput'),
|
||||
dependency('libudev'),
|
||||
dependency('wayland-server'),
|
||||
dependency('xkbcommon'),
|
||||
],
|
||||
install: true,
|
||||
)
|
Loading…
Reference in a new issue