mirror of
https://github.com/hyprwm/hyprland-plugins.git
synced 2026-05-07 19:28:00 +02:00
Merge c7aabf561f into 473804b359
This commit is contained in:
commit
461bb232aa
4 changed files with 603 additions and 110 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -39,4 +39,7 @@ result
|
|||
result-man
|
||||
|
||||
build/
|
||||
.cache/
|
||||
.cache/
|
||||
|
||||
# Claude Code
|
||||
CLAUDE.md
|
||||
|
|
@ -250,9 +250,24 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:bg_col", Hyprlang::INT{0xFF111111});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:workspace_method", Hyprlang::STRING{"center current"});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:skip_empty", Hyprlang::INT{0});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:dynamic_grid", Hyprlang::INT{0});
|
||||
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:gesture_distance", Hyprlang::INT{200});
|
||||
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:active_highlight_col", Hyprlang::INT{0xFF3584E4});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:active_highlight_border", Hyprlang::INT{2});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:hover_highlight_col", Hyprlang::INT{0x80FFFFFF});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:hover_highlight_border", Hyprlang::INT{2});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:label_pos", Hyprlang::STRING{"top_right"});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:label_size", Hyprlang::INT{36});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:label_col", Hyprlang::INT{0xFFFFFFFF});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:show_workspace_names", Hyprlang::INT{0});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:enable_keyboard_nav", Hyprlang::INT{1});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:fill_gaps", Hyprlang::INT{0});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:mru_sort", Hyprlang::INT{0});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:enable_drag_move", Hyprlang::INT{0});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:animate_entry", Hyprlang::INT{0});
|
||||
|
||||
HyprlandAPI::reloadConfig();
|
||||
|
||||
return {"hyprexpo", "A plugin for an overview", "Vaxry", "1.0"};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
#include "overview.hpp"
|
||||
#include <any>
|
||||
#include <cairo/cairo.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <hyprland/src/devices/IKeyboard.hpp>
|
||||
#include <hyprland/src/devices/IPointer.hpp>
|
||||
#define private public
|
||||
#include <hyprland/src/render/Renderer.hpp>
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
|
|
@ -10,11 +15,24 @@
|
|||
#include <hyprland/src/managers/animation/DesktopAnimationManager.hpp>
|
||||
#include <hyprland/src/managers/cursor/CursorShapeOverrideController.hpp>
|
||||
#include <hyprland/src/managers/input/InputManager.hpp>
|
||||
#include <hyprland/src/managers/SeatManager.hpp>
|
||||
#include <hyprland/src/managers/eventLoop/EventLoopManager.hpp>
|
||||
#include <hyprland/src/helpers/time/Time.hpp>
|
||||
#undef private
|
||||
#include "OverviewPassElement.hpp"
|
||||
|
||||
// Compute aspect-correct tile size that fits within a cols x rows grid with gaps
|
||||
static Vector2D aspectCorrectTileSize(double screenW, double screenH, int cols, int rows, double gapSize) {
|
||||
double monAspect = screenW / screenH;
|
||||
double maxTileW = (screenW - gapSize * (cols - 1)) / cols;
|
||||
double maxTileH = (screenH - gapSize * (rows - 1)) / rows;
|
||||
double cellAspect = maxTileW / maxTileH;
|
||||
if (cellAspect > monAspect)
|
||||
return {maxTileH * monAspect, maxTileH};
|
||||
else
|
||||
return {maxTileW, maxTileW / monAspect};
|
||||
}
|
||||
|
||||
static void damageMonitor(WP<Hyprutils::Animation::CBaseAnimatedVariable> thisptr) {
|
||||
g_pOverview->damage();
|
||||
}
|
||||
|
|
@ -27,16 +45,40 @@ COverview::~COverview() {
|
|||
}
|
||||
|
||||
COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn_), swipe(swipe_) {
|
||||
createdAt = std::chrono::steady_clock::now();
|
||||
const auto PMONITOR = Desktop::focusState()->monitor();
|
||||
pMonitor = PMONITOR;
|
||||
|
||||
static auto* const* PCOLUMNS = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:columns")->getDataStaticPtr();
|
||||
static auto* const* PGAPS = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:gap_size")->getDataStaticPtr();
|
||||
static auto* const* PCOL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:bg_col")->getDataStaticPtr();
|
||||
static auto* const* PSKIP = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:skip_empty")->getDataStaticPtr();
|
||||
static auto const* PMETHOD = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:workspace_method")->getDataStaticPtr();
|
||||
static auto* const* PCOLUMNS = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:columns")->getDataStaticPtr();
|
||||
static auto* const* PGAPS = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:gap_size")->getDataStaticPtr();
|
||||
static auto* const* PCOL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:bg_col")->getDataStaticPtr();
|
||||
static auto* const* PSKIP = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:skip_empty")->getDataStaticPtr();
|
||||
static auto* const* PDYNAMIC = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:dynamic_grid")->getDataStaticPtr();
|
||||
static auto const* PMETHOD = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:workspace_method")->getDataStaticPtr();
|
||||
static auto* const* PFILLGAPS = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:fill_gaps")->getDataStaticPtr();
|
||||
static auto* const* PMRUSORT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:mru_sort")->getDataStaticPtr();
|
||||
static auto* const* PSHOWNAMES = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:show_workspace_names")->getDataStaticPtr();
|
||||
|
||||
SIDE_LENGTH = **PCOLUMNS;
|
||||
// Dynamic grid: count active workspaces and calculate optimal grid size
|
||||
if (**PDYNAMIC) {
|
||||
dynamicGrid = true;
|
||||
int activeCount = 0;
|
||||
for (auto& ws : g_pCompositor->m_workspaces) {
|
||||
if (!ws->m_isSpecialWorkspace && ws->getWindows() > 0)
|
||||
activeCount++;
|
||||
}
|
||||
activeCount = std::max(activeCount, 1);
|
||||
SIDE_LENGTH = (int)std::ceil(std::sqrt((double)activeCount)); // columns
|
||||
gridRows = (int)std::ceil((double)activeCount / SIDE_LENGTH); // rows
|
||||
// Ensure minimum 2x2 grid for better aspect ratio (unless only 1 workspace)
|
||||
if (activeCount > 1 && gridRows < 2) {
|
||||
gridRows = 2;
|
||||
}
|
||||
} else {
|
||||
dynamicGrid = false;
|
||||
SIDE_LENGTH = **PCOLUMNS;
|
||||
gridRows = SIDE_LENGTH;
|
||||
}
|
||||
GAP_WIDTH = **PGAPS;
|
||||
BG_COLOR = **PCOL;
|
||||
|
||||
|
|
@ -53,77 +95,115 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn
|
|||
methodStartID = pMonitor->activeWorkspaceID();
|
||||
}
|
||||
|
||||
images.resize(SIDE_LENGTH * SIDE_LENGTH);
|
||||
// For dynamic grid, collect active workspace IDs; otherwise use standard grid
|
||||
if (dynamicGrid) {
|
||||
std::vector<int64_t> activeWorkspaceIDs;
|
||||
for (auto& ws : g_pCompositor->m_workspaces) {
|
||||
if (!ws->m_isSpecialWorkspace && ws->getWindows() > 0)
|
||||
activeWorkspaceIDs.push_back(ws->m_id);
|
||||
}
|
||||
std::sort(activeWorkspaceIDs.begin(), activeWorkspaceIDs.end());
|
||||
if (activeWorkspaceIDs.empty())
|
||||
activeWorkspaceIDs.push_back(pMonitor->activeWorkspaceID());
|
||||
|
||||
// r includes empty workspaces; m skips over them
|
||||
std::string selector = **PSKIP ? "m" : "r";
|
||||
|
||||
if (methodCenter) {
|
||||
int currentID = methodStartID;
|
||||
int firstID = currentID;
|
||||
|
||||
int backtracked = 0;
|
||||
|
||||
// Initialize tiles to WORKSPACE_INVALID; cliking one of these results
|
||||
// in changing to "emptynm" (next empty workspace). Tiles with this id
|
||||
// will only remain if skip_empty is on.
|
||||
for (size_t i = 0; i < images.size(); i++) {
|
||||
images[i].workspaceID = WORKSPACE_INVALID;
|
||||
// Fill gaps: include all workspace IDs between min and max
|
||||
if (**PFILLGAPS && !activeWorkspaceIDs.empty()) {
|
||||
int64_t minID = activeWorkspaceIDs.front();
|
||||
int64_t maxID = activeWorkspaceIDs.back();
|
||||
activeWorkspaceIDs.clear();
|
||||
for (int64_t id = minID; id <= maxID; ++id)
|
||||
activeWorkspaceIDs.push_back(id);
|
||||
}
|
||||
|
||||
// Scan through workspaces lower than methodStartID until we wrap; count how many
|
||||
for (size_t i = 1; i < images.size() / 2; ++i) {
|
||||
currentID = getWorkspaceIDNameFromString(selector + "-" + std::to_string(i)).id;
|
||||
if (currentID >= firstID)
|
||||
break;
|
||||
|
||||
backtracked++;
|
||||
firstID = currentID;
|
||||
}
|
||||
|
||||
// Scan through workspaces higher than methodStartID. If using "m"
|
||||
// (skip_empty), stop when we wrap, leaving the rest of the workspace
|
||||
// ID's set to WORKSPACE_INVALID
|
||||
for (size_t i = 0; i < (size_t)(SIDE_LENGTH * SIDE_LENGTH); ++i) {
|
||||
auto& image = images[i];
|
||||
if ((int64_t)i - backtracked < 0) {
|
||||
currentID = getWorkspaceIDNameFromString(selector + std::to_string((int64_t)i - backtracked)).id;
|
||||
} else {
|
||||
currentID = getWorkspaceIDNameFromString(selector + "+" + std::to_string((int64_t)i - backtracked)).id;
|
||||
if (i > 0 && currentID == firstID)
|
||||
break;
|
||||
// MRU sort: move current workspace to front
|
||||
if (**PMRUSORT) {
|
||||
int64_t currentWsID = startedOn ? startedOn->m_id : pMonitor->activeWorkspaceID();
|
||||
auto it = std::find(activeWorkspaceIDs.begin(), activeWorkspaceIDs.end(), currentWsID);
|
||||
if (it != activeWorkspaceIDs.end() && it != activeWorkspaceIDs.begin()) {
|
||||
int64_t val = *it;
|
||||
activeWorkspaceIDs.erase(it);
|
||||
activeWorkspaceIDs.insert(activeWorkspaceIDs.begin(), val);
|
||||
}
|
||||
image.workspaceID = currentID;
|
||||
}
|
||||
|
||||
// Recalculate grid dimensions after fill_gaps may have changed the count
|
||||
int activeCount = activeWorkspaceIDs.size();
|
||||
SIDE_LENGTH = (int)std::ceil(std::sqrt((double)activeCount));
|
||||
gridRows = (int)std::ceil((double)activeCount / SIDE_LENGTH);
|
||||
if (activeCount > 1 && gridRows < 2)
|
||||
gridRows = 2;
|
||||
|
||||
images.resize(activeWorkspaceIDs.size());
|
||||
for (size_t i = 0; i < activeWorkspaceIDs.size(); ++i)
|
||||
images[i].workspaceID = activeWorkspaceIDs[i];
|
||||
} else {
|
||||
int currentID = methodStartID;
|
||||
images[0].workspaceID = currentID;
|
||||
images.resize(SIDE_LENGTH * SIDE_LENGTH);
|
||||
|
||||
auto PWORKSPACESTART = g_pCompositor->getWorkspaceByID(currentID);
|
||||
if (!PWORKSPACESTART)
|
||||
PWORKSPACESTART = CWorkspace::create(currentID, pMonitor.lock(), std::to_string(currentID));
|
||||
// r includes empty workspaces; m skips over them
|
||||
std::string selector = **PSKIP ? "m" : "r";
|
||||
|
||||
pMonitor->m_activeWorkspace = PWORKSPACESTART;
|
||||
if (methodCenter) {
|
||||
int currentID = methodStartID;
|
||||
int firstID = currentID;
|
||||
|
||||
// Scan through workspaces higher than methodStartID. If using "m"
|
||||
// (skip_empty), stop when we wrap, leaving the rest of the workspace
|
||||
// ID's set to WORKSPACE_INVALID
|
||||
for (size_t i = 1; i < (size_t)(SIDE_LENGTH * SIDE_LENGTH); ++i) {
|
||||
auto& image = images[i];
|
||||
currentID = getWorkspaceIDNameFromString(selector + "+" + std::to_string(i)).id;
|
||||
if (currentID <= methodStartID)
|
||||
break;
|
||||
image.workspaceID = currentID;
|
||||
int backtracked = 0;
|
||||
|
||||
// Initialize tiles to WORKSPACE_INVALID
|
||||
for (size_t i = 0; i < images.size(); i++) {
|
||||
images[i].workspaceID = WORKSPACE_INVALID;
|
||||
}
|
||||
|
||||
// Scan through workspaces lower than methodStartID until we wrap
|
||||
for (size_t i = 1; i < images.size() / 2; ++i) {
|
||||
currentID = getWorkspaceIDNameFromString(selector + "-" + std::to_string(i)).id;
|
||||
if (currentID >= firstID)
|
||||
break;
|
||||
backtracked++;
|
||||
firstID = currentID;
|
||||
}
|
||||
|
||||
// Scan through workspaces higher than methodStartID
|
||||
for (size_t i = 0; i < (size_t)(SIDE_LENGTH * SIDE_LENGTH); ++i) {
|
||||
auto& image = images[i];
|
||||
if ((int64_t)i - backtracked < 0) {
|
||||
currentID = getWorkspaceIDNameFromString(selector + std::to_string((int64_t)i - backtracked)).id;
|
||||
} else {
|
||||
currentID = getWorkspaceIDNameFromString(selector + "+" + std::to_string((int64_t)i - backtracked)).id;
|
||||
if (i > 0 && currentID == firstID)
|
||||
break;
|
||||
}
|
||||
image.workspaceID = currentID;
|
||||
}
|
||||
|
||||
} else {
|
||||
int currentID = methodStartID;
|
||||
images[0].workspaceID = currentID;
|
||||
|
||||
auto PWORKSPACESTART = g_pCompositor->getWorkspaceByID(currentID);
|
||||
if (!PWORKSPACESTART)
|
||||
PWORKSPACESTART = CWorkspace::create(currentID, pMonitor.lock(), std::to_string(currentID));
|
||||
|
||||
pMonitor->m_activeWorkspace = PWORKSPACESTART;
|
||||
|
||||
for (size_t i = 1; i < (size_t)(SIDE_LENGTH * SIDE_LENGTH); ++i) {
|
||||
auto& image = images[i];
|
||||
currentID = getWorkspaceIDNameFromString(selector + "+" + std::to_string(i)).id;
|
||||
if (currentID <= methodStartID)
|
||||
break;
|
||||
image.workspaceID = currentID;
|
||||
}
|
||||
|
||||
pMonitor->m_activeWorkspace = startedOn;
|
||||
}
|
||||
|
||||
pMonitor->m_activeWorkspace = startedOn;
|
||||
}
|
||||
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
|
||||
Vector2D tileSize = pMonitor->m_size / SIDE_LENGTH;
|
||||
Vector2D tileRenderSize = (pMonitor->m_size - Vector2D{GAP_WIDTH * pMonitor->m_scale, GAP_WIDTH * pMonitor->m_scale} * (SIDE_LENGTH - 1)) / SIDE_LENGTH;
|
||||
int cols = SIDE_LENGTH;
|
||||
int rows = dynamicGrid ? gridRows : SIDE_LENGTH;
|
||||
Vector2D tileSize = {pMonitor->m_size.x / cols, pMonitor->m_size.y / rows};
|
||||
double scaledGap = GAP_WIDTH * pMonitor->m_scale;
|
||||
Vector2D tileRenderSize = aspectCorrectTileSize(pMonitor->m_size.x, pMonitor->m_size.y, cols, rows, scaledGap);
|
||||
CBox monbox{0, 0, tileSize.x * 2, tileSize.y * 2};
|
||||
|
||||
if (!ENABLE_LOWRES)
|
||||
|
|
@ -139,7 +219,7 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn
|
|||
|
||||
startedOn->m_visible = false;
|
||||
|
||||
for (size_t i = 0; i < (size_t)(SIDE_LENGTH * SIDE_LENGTH); ++i) {
|
||||
for (size_t i = 0; i < images.size(); ++i) {
|
||||
COverview::SWorkspaceImage& image = images[i];
|
||||
image.fb.alloc(monbox.w, monbox.h, PMONITOR->m_output->state->state().drmFormat);
|
||||
|
||||
|
|
@ -172,13 +252,27 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn
|
|||
} else
|
||||
g_pHyprRenderer->renderWorkspace(PMONITOR, PWORKSPACE, Time::steadyNow(), monbox);
|
||||
|
||||
image.box = {(i % SIDE_LENGTH) * tileRenderSize.x + (i % SIDE_LENGTH) * GAP_WIDTH, (i / SIDE_LENGTH) * tileRenderSize.y + (i / SIDE_LENGTH) * GAP_WIDTH, tileRenderSize.x,
|
||||
tileRenderSize.y};
|
||||
Vector2D tilePos = tilePosForID(i, pMonitor->m_size, scaledGap);
|
||||
image.box = {tilePos.x, tilePos.y, tileRenderSize.x, tileRenderSize.y};
|
||||
|
||||
g_pHyprOpenGL->m_renderData.blockScreenShader = true;
|
||||
g_pHyprRenderer->endRender();
|
||||
}
|
||||
|
||||
// Generate workspace labels - ensure EGL context is current for GL calls
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
static auto* const* PLABELSIZE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:label_size")->getDataStaticPtr();
|
||||
const int LABEL_SIZE = **PLABELSIZE;
|
||||
for (size_t i = 0; i < images.size(); ++i) {
|
||||
images[i].labelTex = makeShared<CTexture>();
|
||||
std::string labelText;
|
||||
if (**PSHOWNAMES && images[i].pWorkspace && !images[i].pWorkspace->m_name.empty())
|
||||
labelText = images[i].pWorkspace->m_name;
|
||||
else
|
||||
labelText = std::to_string(images[i].workspaceID);
|
||||
renderLabel(images[i].labelTex, labelText, LABEL_SIZE);
|
||||
}
|
||||
|
||||
g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
|
||||
|
||||
PMONITOR->m_activeSpecialWorkspace = openSpecial;
|
||||
|
|
@ -189,10 +283,10 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn
|
|||
// zoom on the current workspace.
|
||||
// const auto& TILE = images[std::clamp(currentid, 0, SIDE_LENGTH * SIDE_LENGTH)];
|
||||
|
||||
g_pAnimationManager->createAnimation(pMonitor->m_size * pMonitor->m_size / tileSize, size, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), AVARDAMAGE_NONE);
|
||||
g_pAnimationManager->createAnimation((-((pMonitor->m_size / (double)SIDE_LENGTH) * Vector2D{currentid % SIDE_LENGTH, currentid / SIDE_LENGTH}) * pMonitor->m_scale) *
|
||||
(pMonitor->m_size / tileSize),
|
||||
pos, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), AVARDAMAGE_NONE);
|
||||
Vector2D initSize = pMonitor->m_size * pMonitor->m_size / tileSize;
|
||||
Vector2D initTilePos = tilePosForID(currentid, initSize, 0.0);
|
||||
g_pAnimationManager->createAnimation(initSize, size, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), AVARDAMAGE_NONE);
|
||||
g_pAnimationManager->createAnimation(-(initTilePos * pMonitor->m_scale), pos, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), AVARDAMAGE_NONE);
|
||||
|
||||
size->setUpdateCallback(damageMonitor);
|
||||
pos->setUpdateCallback(damageMonitor);
|
||||
|
|
@ -216,34 +310,184 @@ COverview::COverview(PHLWORKSPACE startedOn_, bool swipe_) : startedOn(startedOn
|
|||
|
||||
info.cancelled = true;
|
||||
lastMousePosLocal = g_pInputManager->getMouseCoordsInternal() - pMonitor->m_position;
|
||||
|
||||
// Check if we should start a drag
|
||||
if (dragSourceID >= 0 && !dragging) {
|
||||
Vector2D delta = lastMousePosLocal - dragStartPos;
|
||||
if (delta.size() > 10.0)
|
||||
dragging = true;
|
||||
}
|
||||
if (dragging)
|
||||
damage();
|
||||
};
|
||||
|
||||
auto onCursorSelect = [this](Event::SCallbackInfo& info) {
|
||||
auto onMouseButton = [this](IPointer::SButtonEvent e, Event::SCallbackInfo& info) {
|
||||
if (closing)
|
||||
return;
|
||||
|
||||
info.cancelled = true;
|
||||
|
||||
static auto* const* PDRAGMOVE_ = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:enable_drag_move")->getDataStaticPtr();
|
||||
|
||||
if (**PDRAGMOVE_) {
|
||||
if (e.state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
// Record drag start
|
||||
dragStartPos = lastMousePosLocal;
|
||||
dragSourceID = -1;
|
||||
dragging = false;
|
||||
for (size_t j = 0; j < images.size(); ++j) {
|
||||
const auto& box = images[j].box;
|
||||
if (lastMousePosLocal.x >= box.x && lastMousePosLocal.x < box.x + box.w &&
|
||||
lastMousePosLocal.y >= box.y && lastMousePosLocal.y < box.y + box.h) {
|
||||
dragSourceID = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Button released
|
||||
if (dragging) {
|
||||
// Find target tile under cursor
|
||||
int targetID = -1;
|
||||
for (size_t j = 0; j < images.size(); ++j) {
|
||||
const auto& box = images[j].box;
|
||||
if (lastMousePosLocal.x >= box.x && lastMousePosLocal.x < box.x + box.w &&
|
||||
lastMousePosLocal.y >= box.y && lastMousePosLocal.y < box.y + box.h) {
|
||||
targetID = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetID >= 0 && targetID != dragSourceID && dragSourceID >= 0) {
|
||||
auto srcWs = images[dragSourceID].pWorkspace;
|
||||
auto dstWs = images[targetID].pWorkspace;
|
||||
if (srcWs && dstWs) {
|
||||
PHLWINDOW topWindow;
|
||||
for (auto& w : g_pCompositor->m_windows) {
|
||||
if (w->m_workspace == srcWs && !w->isHidden() && !w->isX11OverrideRedirect()) {
|
||||
topWindow = w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (topWindow) {
|
||||
g_pCompositor->moveWindowToWorkspaceSafe(topWindow, dstWs);
|
||||
redrawID(dragSourceID);
|
||||
redrawID(targetID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dragging = false;
|
||||
dragSourceID = -1;
|
||||
damage();
|
||||
} else {
|
||||
// Simple click - original behavior
|
||||
dragging = false;
|
||||
dragSourceID = -1;
|
||||
selectHoveredWorkspace();
|
||||
close();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Drag disabled - original behavior on release only
|
||||
if (e.state != WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
selectHoveredWorkspace();
|
||||
close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto onTouchSelect = [this](Event::SCallbackInfo& info) {
|
||||
if (closing)
|
||||
return;
|
||||
|
||||
info.cancelled = true;
|
||||
|
||||
selectHoveredWorkspace();
|
||||
|
||||
close();
|
||||
};
|
||||
|
||||
mouseMoveHook = Event::bus()->m_events.input.mouse.move.listen([onCursorMove](Vector2D, Event::SCallbackInfo& info) { onCursorMove(info); });
|
||||
touchMoveHook = Event::bus()->m_events.input.touch.motion.listen([onCursorMove](ITouch::SMotionEvent, Event::SCallbackInfo& info) { onCursorMove(info); });
|
||||
|
||||
mouseButtonHook = Event::bus()->m_events.input.mouse.button.listen([onCursorSelect](IPointer::SButtonEvent, Event::SCallbackInfo& info) { onCursorSelect(info); });
|
||||
touchDownHook = Event::bus()->m_events.input.touch.down.listen([onCursorSelect](ITouch::SDownEvent, Event::SCallbackInfo& info) { onCursorSelect(info); });
|
||||
mouseButtonHook = Event::bus()->m_events.input.mouse.button.listen(onMouseButton);
|
||||
touchDownHook = Event::bus()->m_events.input.touch.down.listen([onTouchSelect](ITouch::SDownEvent, Event::SCallbackInfo& info) { onTouchSelect(info); });
|
||||
|
||||
static auto* const* PKBNAV = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:enable_keyboard_nav")->getDataStaticPtr();
|
||||
if (**PKBNAV) {
|
||||
selectedID = openedID;
|
||||
keyPressHook = Event::bus()->m_events.input.keyboard.key.listen([this](IKeyboard::SKeyEvent keyEvent, Event::SCallbackInfo& info) {
|
||||
if (closing)
|
||||
return;
|
||||
|
||||
if (keyEvent.state != WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
return;
|
||||
|
||||
auto keyboard = g_pSeatManager->m_keyboard.lock();
|
||||
if (!keyboard)
|
||||
return;
|
||||
xkb_keysym_t sym = xkb_state_key_get_one_sym(keyboard->m_xkbState, keyEvent.keycode + 8);
|
||||
|
||||
int cols = SIDE_LENGTH;
|
||||
int total = (int)images.size();
|
||||
|
||||
switch (sym) {
|
||||
case XKB_KEY_Left:
|
||||
if (selectedID > 0) selectedID--;
|
||||
break;
|
||||
case XKB_KEY_Right:
|
||||
if (selectedID < total - 1) selectedID++;
|
||||
break;
|
||||
case XKB_KEY_Up:
|
||||
if (selectedID - cols >= 0) selectedID -= cols;
|
||||
break;
|
||||
case XKB_KEY_Down:
|
||||
if (selectedID + cols < total) selectedID += cols;
|
||||
break;
|
||||
case XKB_KEY_Return:
|
||||
closeOnID = selectedID;
|
||||
close();
|
||||
break;
|
||||
case XKB_KEY_Escape:
|
||||
close();
|
||||
break;
|
||||
default:
|
||||
if (sym >= XKB_KEY_1 && sym <= XKB_KEY_9) {
|
||||
int idx = sym - XKB_KEY_1;
|
||||
if (idx < total) {
|
||||
closeOnID = idx;
|
||||
close();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
info.cancelled = true;
|
||||
damage();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void COverview::selectHoveredWorkspace() {
|
||||
if (closing)
|
||||
return;
|
||||
|
||||
// get tile x,y
|
||||
int x = lastMousePosLocal.x / pMonitor->m_size.x * SIDE_LENGTH;
|
||||
int y = lastMousePosLocal.y / pMonitor->m_size.y * SIDE_LENGTH;
|
||||
closeOnID = x + y * SIDE_LENGTH;
|
||||
// Check each tile's actual box for hit detection (handles centered partial rows)
|
||||
for (size_t i = 0; i < images.size(); ++i) {
|
||||
const auto& box = images[i].box;
|
||||
if (lastMousePosLocal.x >= box.x && lastMousePosLocal.x < box.x + box.w &&
|
||||
lastMousePosLocal.y >= box.y && lastMousePosLocal.y < box.y + box.h) {
|
||||
closeOnID = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: use grid calculation and clamp to nearest valid tile
|
||||
int cols = SIDE_LENGTH;
|
||||
int rows = dynamicGrid ? gridRows : SIDE_LENGTH;
|
||||
int x = lastMousePosLocal.x / pMonitor->m_size.x * cols;
|
||||
int y = lastMousePosLocal.y / pMonitor->m_size.y * rows;
|
||||
int idx = x + y * cols;
|
||||
closeOnID = std::clamp(idx, 0, (int)images.size() - 1);
|
||||
}
|
||||
|
||||
void COverview::redrawID(int id, bool forcelowres) {
|
||||
|
|
@ -259,10 +503,13 @@ void COverview::redrawID(int id, bool forcelowres) {
|
|||
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
|
||||
id = std::clamp(id, 0, SIDE_LENGTH * SIDE_LENGTH);
|
||||
id = std::clamp(id, 0, (int)images.size() - 1);
|
||||
|
||||
Vector2D tileSize = pMonitor->m_size / SIDE_LENGTH;
|
||||
Vector2D tileRenderSize = (pMonitor->m_size - Vector2D{GAP_WIDTH, GAP_WIDTH} * (SIDE_LENGTH - 1)) / SIDE_LENGTH;
|
||||
int cols = SIDE_LENGTH;
|
||||
int rows = dynamicGrid ? gridRows : SIDE_LENGTH;
|
||||
Vector2D tileSize = {pMonitor->m_size.x / cols, pMonitor->m_size.y / rows};
|
||||
Vector2D tileRenderSize = {(pMonitor->m_size.x - GAP_WIDTH * (cols - 1)) / cols,
|
||||
(pMonitor->m_size.y - GAP_WIDTH * (rows - 1)) / rows};
|
||||
CBox monbox{0, 0, tileSize.x * 2, tileSize.y * 2};
|
||||
|
||||
if (!forcelowres && (size->value() != pMonitor->m_size || closing))
|
||||
|
|
@ -323,7 +570,7 @@ void COverview::redrawID(int id, bool forcelowres) {
|
|||
void COverview::redrawAll(bool forcelowres) {
|
||||
if (!pMonitor)
|
||||
return;
|
||||
for (size_t i = 0; i < (size_t)(SIDE_LENGTH * SIDE_LENGTH); ++i) {
|
||||
for (size_t i = 0; i < images.size(); ++i) {
|
||||
redrawID(i, forcelowres);
|
||||
}
|
||||
}
|
||||
|
|
@ -339,12 +586,12 @@ void COverview::onDamageReported() {
|
|||
|
||||
Vector2D SIZE = size->value();
|
||||
|
||||
Vector2D tileSize = (SIZE / SIDE_LENGTH);
|
||||
Vector2D tileRenderSize = (SIZE - Vector2D{GAP_WIDTH, GAP_WIDTH} * (SIDE_LENGTH - 1)) / SIDE_LENGTH;
|
||||
// const auto& TILE = images[std::clamp(openedID, 0, SIDE_LENGTH * SIDE_LENGTH)];
|
||||
CBox texbox = CBox{(openedID % SIDE_LENGTH) * tileRenderSize.x + (openedID % SIDE_LENGTH) * GAP_WIDTH,
|
||||
(openedID / SIDE_LENGTH) * tileRenderSize.y + (openedID / SIDE_LENGTH) * GAP_WIDTH, tileRenderSize.x, tileRenderSize.y}
|
||||
.translate(pMonitor->m_position);
|
||||
int cols = SIDE_LENGTH;
|
||||
int rows = dynamicGrid ? gridRows : SIDE_LENGTH;
|
||||
Vector2D tileSize = {SIZE.x / cols, SIZE.y / rows};
|
||||
Vector2D tileRenderSize = aspectCorrectTileSize(SIZE.x, SIZE.y, cols, rows, (double)GAP_WIDTH);
|
||||
Vector2D tilePos = tilePosForID(openedID, SIZE, (double)GAP_WIDTH);
|
||||
CBox texbox = CBox{tilePos.x, tilePos.y, tileRenderSize.x, tileRenderSize.y}.translate(pMonitor->m_position);
|
||||
|
||||
damage();
|
||||
|
||||
|
|
@ -354,21 +601,53 @@ void COverview::onDamageReported() {
|
|||
g_pCompositor->scheduleFrameForMonitor(pMonitor.lock());
|
||||
}
|
||||
|
||||
Vector2D COverview::tilePosForID(int id, Vector2D totalSize, double gapSize) const {
|
||||
int cols = SIDE_LENGTH;
|
||||
int rows = dynamicGrid ? gridRows : SIDE_LENGTH;
|
||||
Vector2D tileRenderSize = aspectCorrectTileSize(totalSize.x, totalSize.y, cols, rows, gapSize);
|
||||
|
||||
int tilesInLastRow = images.size() % cols;
|
||||
if (tilesInLastRow == 0)
|
||||
tilesInLastRow = cols;
|
||||
int lastRow = ((int)images.size() - 1) / cols;
|
||||
int actualRows = lastRow + 1;
|
||||
double gridW = cols * tileRenderSize.x + (cols - 1) * gapSize;
|
||||
double gridH = actualRows * tileRenderSize.y + (actualRows - 1) * gapSize;
|
||||
double baseOffX = (totalSize.x - gridW) / 2.0;
|
||||
double baseOffY = (totalSize.y - gridH) / 2.0;
|
||||
|
||||
int x = id % cols;
|
||||
int y = id / cols;
|
||||
double tileX;
|
||||
if (y == lastRow && tilesInLastRow < cols) {
|
||||
double rowW = tilesInLastRow * tileRenderSize.x + (tilesInLastRow - 1) * gapSize;
|
||||
tileX = (totalSize.x - rowW) / 2.0 + x * (tileRenderSize.x + gapSize);
|
||||
} else {
|
||||
tileX = baseOffX + x * (tileRenderSize.x + gapSize);
|
||||
}
|
||||
double tileY = baseOffY + y * (tileRenderSize.y + gapSize);
|
||||
|
||||
return {tileX, tileY};
|
||||
}
|
||||
|
||||
void COverview::close() {
|
||||
if (closing)
|
||||
return;
|
||||
|
||||
const int ID = closeOnID == -1 ? openedID : closeOnID;
|
||||
|
||||
const auto& TILE = images[std::clamp(ID, 0, SIDE_LENGTH * SIDE_LENGTH)];
|
||||
const auto& TILE = images[std::clamp(ID, 0, (int)images.size() - 1)];
|
||||
|
||||
Vector2D tileSize = (pMonitor->m_size / SIDE_LENGTH);
|
||||
int cols = SIDE_LENGTH;
|
||||
int rows = dynamicGrid ? gridRows : SIDE_LENGTH;
|
||||
Vector2D tileSize = {pMonitor->m_size.x / cols, pMonitor->m_size.y / rows};
|
||||
Vector2D targetSize = pMonitor->m_size * pMonitor->m_size / tileSize;
|
||||
|
||||
size->warp();
|
||||
pos->warp();
|
||||
|
||||
*size = pMonitor->m_size * pMonitor->m_size / tileSize;
|
||||
*pos = (-((pMonitor->m_size / (double)SIDE_LENGTH) * Vector2D{ID % SIDE_LENGTH, ID / SIDE_LENGTH}) * pMonitor->m_scale) * (pMonitor->m_size / tileSize);
|
||||
*size = targetSize;
|
||||
*pos = -(tilePosForID(ID, targetSize, 0.0) * pMonitor->m_scale);
|
||||
|
||||
closing = true;
|
||||
|
||||
|
|
@ -413,7 +692,7 @@ void COverview::onWorkspaceChange() {
|
|||
else
|
||||
startedOn = pMonitor->m_activeWorkspace;
|
||||
|
||||
for (size_t i = 0; i < (size_t)(SIDE_LENGTH * SIDE_LENGTH); ++i) {
|
||||
for (size_t i = 0; i < images.size(); ++i) {
|
||||
if (images[i].workspaceID != pMonitor->activeWorkspaceID())
|
||||
continue;
|
||||
|
||||
|
|
@ -440,22 +719,198 @@ void COverview::fullRender() {
|
|||
|
||||
Vector2D SIZE = size->value();
|
||||
|
||||
Vector2D tileSize = (SIZE / SIDE_LENGTH);
|
||||
Vector2D tileRenderSize = (SIZE - Vector2D{GAPSIZE, GAPSIZE} * (SIDE_LENGTH - 1)) / SIDE_LENGTH;
|
||||
int cols = SIDE_LENGTH;
|
||||
int rows = dynamicGrid ? gridRows : SIDE_LENGTH;
|
||||
Vector2D tileSize = {SIZE.x / cols, SIZE.y / rows};
|
||||
Vector2D tileRenderSize = aspectCorrectTileSize(SIZE.x, SIZE.y, cols, rows, GAPSIZE);
|
||||
|
||||
g_pHyprOpenGL->clear(BG_COLOR.stripA());
|
||||
|
||||
for (size_t y = 0; y < (size_t)SIDE_LENGTH; ++y) {
|
||||
for (size_t x = 0; x < (size_t)SIDE_LENGTH; ++x) {
|
||||
CBox texbox = {x * tileRenderSize.x + x * GAPSIZE, y * tileRenderSize.y + y * GAPSIZE, tileRenderSize.x, tileRenderSize.y};
|
||||
texbox.scale(pMonitor->m_scale).translate(pos->value());
|
||||
texbox.round();
|
||||
CRegion damage{0, 0, INT16_MAX, INT16_MAX};
|
||||
g_pHyprOpenGL->renderTextureInternal(images[x + y * SIDE_LENGTH].fb.getTexture(), texbox, {.damage = &damage, .a = 1.0});
|
||||
for (size_t i = 0; i < images.size(); ++i) {
|
||||
Vector2D tilePos = tilePosForID(i, SIZE, GAPSIZE);
|
||||
CBox texbox = {tilePos.x, tilePos.y, tileRenderSize.x, tileRenderSize.y};
|
||||
texbox.scale(pMonitor->m_scale).translate(pos->value());
|
||||
texbox.round();
|
||||
CRegion damage{0, 0, INT16_MAX, INT16_MAX};
|
||||
|
||||
// Staggered fade-in animation
|
||||
static auto* const* PANIMATE_R = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:animate_entry")->getDataStaticPtr();
|
||||
float tileAlpha = 1.0f;
|
||||
if (**PANIMATE_R && !closing) {
|
||||
auto elapsed = std::chrono::duration<float>(std::chrono::steady_clock::now() - createdAt).count();
|
||||
float delay = i * 0.05f;
|
||||
float duration = 0.2f;
|
||||
if (elapsed < delay)
|
||||
tileAlpha = 0.0f;
|
||||
else if (elapsed < delay + duration)
|
||||
tileAlpha = (elapsed - delay) / duration;
|
||||
else
|
||||
tileAlpha = 1.0f;
|
||||
|
||||
if (tileAlpha < 1.0f)
|
||||
this->damage();
|
||||
}
|
||||
|
||||
g_pHyprOpenGL->renderTextureInternal(images[i].fb.getTexture(), texbox, {.damage = &damage, .a = tileAlpha});
|
||||
|
||||
// Active workspace highlight
|
||||
static auto* const* PACTIVECOL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:active_highlight_col")->getDataStaticPtr();
|
||||
static auto* const* PACTIVEBORDER = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:active_highlight_border")->getDataStaticPtr();
|
||||
static auto* const* PHOVERCOL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:hover_highlight_col")->getDataStaticPtr();
|
||||
static auto* const* PHOVERBORDER = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:hover_highlight_border")->getDataStaticPtr();
|
||||
|
||||
if (images[i].pWorkspace == startedOn) {
|
||||
CHyprColor activeCol = **PACTIVECOL;
|
||||
int borderW = **PACTIVEBORDER * pMonitor->m_scale;
|
||||
CBox top = {texbox.x, texbox.y, texbox.w, borderW};
|
||||
CBox bottom = {texbox.x, texbox.y + texbox.h - borderW, texbox.w, borderW};
|
||||
CBox left = {texbox.x, texbox.y, borderW, texbox.h};
|
||||
CBox right = {texbox.x + texbox.w - borderW, texbox.y, borderW, texbox.h};
|
||||
g_pHyprOpenGL->renderRect(top, activeCol, {.damage = &damage});
|
||||
g_pHyprOpenGL->renderRect(bottom, activeCol, {.damage = &damage});
|
||||
g_pHyprOpenGL->renderRect(left, activeCol, {.damage = &damage});
|
||||
g_pHyprOpenGL->renderRect(right, activeCol, {.damage = &damage});
|
||||
}
|
||||
|
||||
// Drag visual feedback: dim the source tile
|
||||
if (dragging && dragSourceID == (int)i) {
|
||||
CHyprColor dimCol{0.0f, 0.0f, 0.0f, 0.3f};
|
||||
g_pHyprOpenGL->renderRect(texbox, dimCol, {.damage = &damage});
|
||||
}
|
||||
|
||||
// Hover highlight (also used as drop target indicator during drag)
|
||||
bool isHovered = (lastMousePosLocal.x >= images[i].box.x && lastMousePosLocal.x < images[i].box.x + images[i].box.w &&
|
||||
lastMousePosLocal.y >= images[i].box.y && lastMousePosLocal.y < images[i].box.y + images[i].box.h);
|
||||
if (isHovered && (dragging ? (int)i != dragSourceID : images[i].pWorkspace != startedOn)) {
|
||||
CHyprColor hoverCol = **PHOVERCOL;
|
||||
int borderW = **PHOVERBORDER * pMonitor->m_scale;
|
||||
CBox top = {texbox.x, texbox.y, texbox.w, borderW};
|
||||
CBox bottom = {texbox.x, texbox.y + texbox.h - borderW, texbox.w, borderW};
|
||||
CBox left = {texbox.x, texbox.y, borderW, texbox.h};
|
||||
CBox right = {texbox.x + texbox.w - borderW, texbox.y, borderW, texbox.h};
|
||||
g_pHyprOpenGL->renderRect(top, hoverCol, {.damage = &damage});
|
||||
g_pHyprOpenGL->renderRect(bottom, hoverCol, {.damage = &damage});
|
||||
g_pHyprOpenGL->renderRect(left, hoverCol, {.damage = &damage});
|
||||
g_pHyprOpenGL->renderRect(right, hoverCol, {.damage = &damage});
|
||||
}
|
||||
|
||||
// Keyboard selection indicator
|
||||
if (selectedID >= 0 && selectedID == (int)i) {
|
||||
CHyprColor selCol{1.0f, 1.0f, 1.0f, 0.9f};
|
||||
int selBorderW = 3 * pMonitor->m_scale;
|
||||
CBox top = {texbox.x, texbox.y, texbox.w, selBorderW};
|
||||
CBox bottom = {texbox.x, texbox.y + texbox.h - selBorderW, texbox.w, selBorderW};
|
||||
CBox left = {texbox.x, texbox.y, selBorderW, texbox.h};
|
||||
CBox right = {texbox.x + texbox.w - selBorderW, texbox.y, selBorderW, texbox.h};
|
||||
g_pHyprOpenGL->renderRect(top, selCol, {.damage = &damage});
|
||||
g_pHyprOpenGL->renderRect(bottom, selCol, {.damage = &damage});
|
||||
g_pHyprOpenGL->renderRect(left, selCol, {.damage = &damage});
|
||||
g_pHyprOpenGL->renderRect(right, selCol, {.damage = &damage});
|
||||
}
|
||||
|
||||
// Render workspace label
|
||||
if (images[i].labelTex && images[i].labelTex->m_texID) {
|
||||
static auto* const* PLABELSIZE2 = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:label_size")->getDataStaticPtr();
|
||||
static auto const* PLABELPOS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:label_pos")->getDataStaticPtr();
|
||||
|
||||
const float labelSize = **PLABELSIZE2 * pMonitor->m_scale;
|
||||
const float padding = 8 * pMonitor->m_scale;
|
||||
const std::string posStr = *PLABELPOS;
|
||||
|
||||
float lx, ly;
|
||||
if (posStr == "top_left") {
|
||||
lx = texbox.x + padding;
|
||||
ly = texbox.y + padding;
|
||||
} else if (posStr == "bottom_right") {
|
||||
lx = texbox.x + texbox.w - labelSize - padding;
|
||||
ly = texbox.y + texbox.h - labelSize - padding;
|
||||
} else if (posStr == "bottom_left") {
|
||||
lx = texbox.x + padding;
|
||||
ly = texbox.y + texbox.h - labelSize - padding;
|
||||
} else { // "top_right" (default)
|
||||
lx = texbox.x + texbox.w - labelSize - padding;
|
||||
ly = texbox.y + padding;
|
||||
}
|
||||
|
||||
CBox labelBox = {lx, ly, labelSize, labelSize};
|
||||
labelBox.round();
|
||||
g_pHyprOpenGL->renderTextureInternal(images[i].labelTex, labelBox, {.damage = &damage, .a = 0.9f * tileAlpha});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void COverview::renderLabel(SP<CTexture>& tex, const std::string& text, int size) {
|
||||
if (!tex)
|
||||
return;
|
||||
|
||||
const auto PMONITOR = pMonitor.lock();
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
const float scale = PMONITOR->m_scale;
|
||||
const int bufferSize = std::max(1, (int)(size * scale));
|
||||
|
||||
const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize, bufferSize);
|
||||
if (cairo_surface_status(CAIROSURFACE) != CAIRO_STATUS_SUCCESS) {
|
||||
cairo_surface_destroy(CAIROSURFACE);
|
||||
return;
|
||||
}
|
||||
const auto CAIRO = cairo_create(CAIROSURFACE);
|
||||
|
||||
// Clear the surface
|
||||
cairo_save(CAIRO);
|
||||
cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(CAIRO);
|
||||
cairo_restore(CAIRO);
|
||||
|
||||
// Draw semi-transparent dark background circle
|
||||
cairo_set_source_rgba(CAIRO, 0.0, 0.0, 0.0, 0.7);
|
||||
cairo_arc(CAIRO, bufferSize / 2.0, bufferSize / 2.0, bufferSize / 2.0 - 2, 0, 2 * M_PI);
|
||||
cairo_fill(CAIRO);
|
||||
|
||||
// Draw text using Pango
|
||||
PangoLayout* layout = pango_cairo_create_layout(CAIRO);
|
||||
pango_layout_set_text(layout, text.c_str(), -1);
|
||||
|
||||
PangoFontDescription* fontDesc = pango_font_description_from_string("sans bold");
|
||||
pango_font_description_set_size(fontDesc, (int)(size * 0.5 * scale * PANGO_SCALE));
|
||||
pango_layout_set_font_description(layout, fontDesc);
|
||||
pango_font_description_free(fontDesc);
|
||||
|
||||
static auto* const* PLABELCOL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:label_col")->getDataStaticPtr();
|
||||
CHyprColor labelColor = **PLABELCOL;
|
||||
cairo_set_source_rgba(CAIRO, labelColor.r, labelColor.g, labelColor.b, labelColor.a);
|
||||
|
||||
PangoRectangle ink_rect, logical_rect;
|
||||
pango_layout_get_extents(layout, &ink_rect, &logical_rect);
|
||||
|
||||
const double xOffset = (bufferSize / 2.0 - logical_rect.width / PANGO_SCALE / 2.0);
|
||||
const double yOffset = (bufferSize / 2.0 - logical_rect.height / PANGO_SCALE / 2.0);
|
||||
|
||||
cairo_move_to(CAIRO, xOffset, yOffset);
|
||||
pango_cairo_show_layout(CAIRO, layout);
|
||||
|
||||
g_object_unref(layout);
|
||||
cairo_surface_flush(CAIROSURFACE);
|
||||
|
||||
// Upload to OpenGL texture
|
||||
const auto DATA = cairo_image_surface_get_data(CAIROSURFACE);
|
||||
tex->allocate();
|
||||
glBindTexture(GL_TEXTURE_2D, tex->m_texID);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
#ifndef GLES2
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
#endif
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferSize, bufferSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
||||
|
||||
cairo_destroy(CAIRO);
|
||||
cairo_surface_destroy(CAIROSURFACE);
|
||||
}
|
||||
|
||||
static float lerp(const float& from, const float& to, const float perc) {
|
||||
return (to - from) * perc + from;
|
||||
}
|
||||
|
|
@ -480,11 +935,12 @@ void COverview::onSwipeUpdate(double delta) {
|
|||
const float PERC = closing ? std::clamp(delta / (double)**PDISTANCE, 0.0, 1.0) : 1.0 - std::clamp(delta / (double)**PDISTANCE, 0.0, 1.0);
|
||||
const auto WORKSPACE_FOCUS_ID = closing && closeOnID != -1 ? closeOnID : openedID;
|
||||
|
||||
Vector2D tileSize = (pMonitor->m_size / SIDE_LENGTH);
|
||||
int cols = SIDE_LENGTH;
|
||||
int rows = dynamicGrid ? gridRows : SIDE_LENGTH;
|
||||
Vector2D tileSize = {pMonitor->m_size.x / cols, pMonitor->m_size.y / rows};
|
||||
|
||||
const auto SIZEMAX = pMonitor->m_size * pMonitor->m_size / tileSize;
|
||||
const auto POSMAX = (-((pMonitor->m_size / (double)SIDE_LENGTH) * Vector2D{WORKSPACE_FOCUS_ID % SIDE_LENGTH, WORKSPACE_FOCUS_ID / SIDE_LENGTH}) * pMonitor->m_scale) *
|
||||
(pMonitor->m_size / tileSize);
|
||||
const auto POSMAX = -(tilePosForID(WORKSPACE_FOCUS_ID, SIZEMAX, 0.0) * pMonitor->m_scale);
|
||||
|
||||
const auto SIZEMIN = pMonitor->m_size;
|
||||
const auto POSMIN = Vector2D{0, 0};
|
||||
|
|
@ -497,8 +953,11 @@ void COverview::onSwipeEnd() {
|
|||
if (closing || !m_isSwiping)
|
||||
return;
|
||||
|
||||
int cols = SIDE_LENGTH;
|
||||
int rows = dynamicGrid ? gridRows : SIDE_LENGTH;
|
||||
Vector2D tileSize = {pMonitor->m_size.x / cols, pMonitor->m_size.y / rows};
|
||||
const auto SIZEMIN = pMonitor->m_size;
|
||||
const auto SIZEMAX = pMonitor->m_size * pMonitor->m_size / (pMonitor->m_size / SIDE_LENGTH);
|
||||
const auto SIZEMAX = pMonitor->m_size * pMonitor->m_size / tileSize;
|
||||
const auto PERC = (size->value() - SIZEMIN).x / (SIZEMAX - SIZEMIN).x;
|
||||
if (PERC > 0.5) {
|
||||
close();
|
||||
|
|
@ -507,7 +966,7 @@ void COverview::onSwipeEnd() {
|
|||
*size = pMonitor->m_size;
|
||||
*pos = {0, 0};
|
||||
|
||||
size->setCallbackOnEnd([this](WP<Hyprutils::Animation::CBaseAnimatedVariable> thisptr) { redrawAll(true); });
|
||||
size->setCallbackOnEnd([this](auto) { redrawAll(true); });
|
||||
|
||||
swipeWasCommenced = false;
|
||||
m_isSwiping = false;
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@
|
|||
#include "globals.hpp"
|
||||
#include <hyprland/src/desktop/DesktopTypes.hpp>
|
||||
#include <hyprland/src/render/Framebuffer.hpp>
|
||||
#include <hyprland/src/render/Texture.hpp>
|
||||
#include <hyprland/src/helpers/AnimatedVariable.hpp>
|
||||
#include <hyprland/src/event/EventBus.hpp>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
|
||||
// saves on resources, but is a bit broken rn with blur.
|
||||
|
|
@ -46,15 +48,20 @@ class COverview {
|
|||
void redrawAll(bool forcelowres = false);
|
||||
void onWorkspaceChange();
|
||||
void fullRender();
|
||||
void renderLabel(SP<CTexture>& tex, const std::string& text, int size);
|
||||
Vector2D tilePosForID(int id, Vector2D totalSize, double gapSize) const;
|
||||
|
||||
int SIDE_LENGTH = 3;
|
||||
int GAP_WIDTH = 5;
|
||||
CHyprColor BG_COLOR = CHyprColor{0.1, 0.1, 0.1, 1.0};
|
||||
int SIDE_LENGTH = 3; // columns in grid
|
||||
int GAP_WIDTH = 5;
|
||||
CHyprColor BG_COLOR = CHyprColor{0.1, 0.1, 0.1, 1.0};
|
||||
int gridRows = 3; // rows in dynamic grid
|
||||
bool dynamicGrid = false;
|
||||
|
||||
bool damageDirty = false;
|
||||
|
||||
struct SWorkspaceImage {
|
||||
CFramebuffer fb;
|
||||
SP<CTexture> labelTex;
|
||||
int64_t workspaceID = -1;
|
||||
PHLWORKSPACE pWorkspace;
|
||||
CBox box;
|
||||
|
|
@ -82,6 +89,15 @@ class COverview {
|
|||
bool swipe = false;
|
||||
bool swipeWasCommenced = false;
|
||||
|
||||
int hoveredID = -1;
|
||||
int selectedID = -1;
|
||||
CHyprSignalListener keyPressHook;
|
||||
bool dragging = false;
|
||||
int dragSourceID = -1;
|
||||
Vector2D dragStartPos;
|
||||
|
||||
std::chrono::steady_clock::time_point createdAt;
|
||||
|
||||
friend class COverviewPassElement;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue