Hi
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.o
|
||||
*.so
|
||||
15
Makefile
Normal file
15
Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
PLUGIN_NAME=hypreasymotion
|
||||
INSTALL_LOCATION=${HOME}/.local/share/hyprload/plugins/bin
|
||||
SOURCE_FILES=$(wildcard ./*.cpp)
|
||||
|
||||
all: $(PLUGIN_NAME).so
|
||||
|
||||
install: all
|
||||
mkdir -p ${INSTALL_LOCATION}
|
||||
cp $(PLUGIN_NAME).so ${INSTALL_LOCATION}
|
||||
|
||||
$(PLUGIN_NAME).so: $(SOURCE_FILES)
|
||||
g++ -shared -Wall -fPIC --no-gnu-unique $(SOURCE_FILES) -g -DWLR_USE_UNSTABLE `pkg-config --cflags pixman-1 libdrm hyprland pangocairo` -std=c++23 -o $(PLUGIN_NAME).so
|
||||
|
||||
clean:
|
||||
rm -f ./$(PLUGIN_NAME).so
|
||||
68
README.md
Normal file
68
README.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# hyprEasymotion
|
||||
Plugin to enable 'easymotion' navigation. Inspired by Xmonad easymotion (which is in turn inspired by vim-easymotion)
|
||||
|
||||
# Configuration
|
||||
Easymotion is basically a single dispatcher that brings up window labels and then allows you to execute a user-defined command when one of those labels is typed.
|
||||
|
||||
`bind=SUPER,z,action:hyprctl dispatch focuswindow address:{}`
|
||||
|
||||
This bind will bring up easymotion with SUPER-z. Once you select a window the window
|
||||
will focus. If you want to change the command, the selected window's address is substituted where "{}" occurs.
|
||||
|
||||
|
||||
You can configure the appearance of the labels. Defaults are as follows:
|
||||
|
||||
```
|
||||
plugin {
|
||||
easymotion {
|
||||
#font size of the text
|
||||
textsize=15
|
||||
|
||||
#color of the text, takes standard hyprland color format
|
||||
textcolor=rgba(ffffffff)
|
||||
|
||||
#background color of the label box. alpha is respected
|
||||
bgcolor=rgba(000000ff)
|
||||
|
||||
#font to use for the label. This is passed directly to the pango font description
|
||||
textfont=Sans
|
||||
|
||||
#padding around the text (inside the label box) size in pixels, adjusted for
|
||||
#monitor scaling. This is the same format as hyprland's gaps_in/gaps_out
|
||||
textpadding=0
|
||||
|
||||
#size of the border around the label box
|
||||
bordersize=0
|
||||
|
||||
#color of the border. takes the same format as hyprland's border (so it can be a gradient)
|
||||
bordercolor=rgba(ffffffff)
|
||||
|
||||
#rounded corners? Same as hyprland's 'decoration:rounding' config
|
||||
rounding=0
|
||||
|
||||
#which keys to use for labeling windows
|
||||
motionkeys=abcdefghijklmnopqrstuvwxyz1234567890
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Every one of these variables is also settable via the dispatcher, so you can create multiple dispatchers that look different based on function.
|
||||
|
||||
`bind=SUPER,z,bgcolor=rgba(ff0000ff);bordersize=5;action:hyprctl dispatch closewindow address:{}`
|
||||
|
||||
### IMPORTANT
|
||||
The easymotion arguments are separated by a semicolon, not a comma. (gap/padding format uses commas :/)
|
||||
|
||||
# Installing
|
||||
|
||||
## Hyprpm, Hyprland's official plugin manager (recommended)
|
||||
1. Run `hyprpm add https://github.com/zakk4223/hyprland-easymotion` and wait for hyprpm to build the plugin.
|
||||
2. Run `hyprpm enable hyprEasymotion`
|
||||
|
||||
|
||||
# TODO
|
||||
- [ ] Blur?
|
||||
- [ ] Allow multi-letter labels?
|
||||
- [ ] Fixed/static label box sizing
|
||||
- [ ] Location of label in window (edges etc)
|
||||
- [ ] Auto label placement that tries to avoid being occluded
|
||||
198
easymotionDeco.cpp
Normal file
198
easymotionDeco.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
#include "easymotionDeco.hpp"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/desktop/Window.hpp>
|
||||
#include <hyprland/src/render/decorations/IHyprWindowDecoration.hpp>
|
||||
#include <pango/pangocairo.h>
|
||||
|
||||
#include "globals.hpp"
|
||||
|
||||
CHyprEasyLabel::CHyprEasyLabel(CWindow* pWindow, SMotionActionDesc *actionDesc) : IHyprWindowDecoration(pWindow) {
|
||||
m_pWindow = pWindow;
|
||||
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
|
||||
PMONITOR->scheduledRecalc = true;
|
||||
std::string windowAddr = std::format("0x{:x}", (uintptr_t)pWindow);
|
||||
m_szActionCmd = std::vformat(actionDesc->commandString, std::make_format_args(windowAddr));
|
||||
m_iTextSize = actionDesc->textSize;
|
||||
m_cTextColor = actionDesc->textColor;
|
||||
m_cBackgroundColor = actionDesc->backgroundColor;
|
||||
m_szTextFont = actionDesc->textFont;
|
||||
m_iPaddingTop = actionDesc->boxPadding.top;
|
||||
m_iPaddingBottom = actionDesc->boxPadding.bottom;
|
||||
m_iPaddingRight = actionDesc->boxPadding.right;
|
||||
m_iPaddingLeft = actionDesc->boxPadding.left;
|
||||
m_iRounding = actionDesc->rounding;
|
||||
m_iBorderSize = actionDesc->borderSize;
|
||||
m_cBorderGradient = actionDesc->borderColor;
|
||||
}
|
||||
|
||||
CHyprEasyLabel::~CHyprEasyLabel() {
|
||||
damageEntire();
|
||||
std::erase(g_pGlobalState->motionLabels, this);
|
||||
}
|
||||
|
||||
SDecorationPositioningInfo CHyprEasyLabel::getPositioningInfo() {
|
||||
SDecorationPositioningInfo info;
|
||||
info.policy = DECORATION_POSITION_ABSOLUTE;
|
||||
return info;
|
||||
}
|
||||
|
||||
void CHyprEasyLabel::onPositioningReply(const SDecorationPositioningReply& reply) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string CHyprEasyLabel::getDisplayName() {
|
||||
return "EasyMotion";
|
||||
}
|
||||
|
||||
void CHyprEasyLabel::renderMotionString(Vector2D& bufferSize, const float scale) {
|
||||
int textSize = m_iTextSize;
|
||||
const auto scaledSize = textSize * scale;
|
||||
const auto textColor = CColor(m_cTextColor);
|
||||
|
||||
const auto LAYOUTSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0);
|
||||
const auto LAYOUTCAIRO = cairo_create(LAYOUTSURFACE);
|
||||
cairo_surface_destroy(LAYOUTSURFACE);
|
||||
|
||||
PangoLayout *layout = pango_cairo_create_layout(LAYOUTCAIRO);
|
||||
pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
|
||||
pango_layout_set_text(layout, m_szLabel.c_str(), -1);
|
||||
PangoFontDescription *fontDesc = pango_font_description_from_string(m_szTextFont.c_str());
|
||||
|
||||
pango_font_description_set_size(fontDesc, scaledSize * PANGO_SCALE);
|
||||
pango_layout_set_font_description(layout, fontDesc);
|
||||
pango_font_description_free(fontDesc);
|
||||
|
||||
PangoRectangle ink_rect;
|
||||
PangoRectangle logical_rect;
|
||||
pango_layout_get_pixel_extents(layout, &ink_rect, &logical_rect);
|
||||
Debug::log(LOG, "INK {} {} {} {} LOGICAL {} {} {} {}", ink_rect.x, ink_rect.y, ink_rect.width, ink_rect.height, logical_rect.x, logical_rect.y, logical_rect.width, logical_rect.height);
|
||||
|
||||
layoutWidth = ink_rect.width+m_iPaddingLeft+m_iPaddingRight;
|
||||
layoutHeight = ink_rect.height+m_iPaddingTop+m_iPaddingBottom;
|
||||
|
||||
bufferSize.x = layoutWidth*scale;
|
||||
bufferSize.y = layoutHeight*scale;
|
||||
|
||||
const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y);
|
||||
const auto CAIRO = cairo_create(CAIROSURFACE);
|
||||
|
||||
cairo_save(CAIRO);
|
||||
cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(CAIRO);
|
||||
cairo_restore(CAIRO);
|
||||
cairo_move_to(CAIRO, -ink_rect.x+m_iPaddingLeft, -ink_rect.y+m_iPaddingTop);
|
||||
cairo_set_source_rgba(CAIRO, textColor.r, textColor.g, textColor.b, textColor.a);
|
||||
pango_cairo_show_layout(CAIRO, layout);
|
||||
g_object_unref(layout);
|
||||
cairo_surface_flush(CAIROSURFACE);
|
||||
const auto DATA = cairo_image_surface_get_data(CAIROSURFACE);
|
||||
m_tTextTex.allocate();
|
||||
glBindTexture(GL_TEXTURE_2D, m_tTextTex.m_iTexID);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
#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.x, bufferSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
||||
|
||||
// delete cairo
|
||||
cairo_destroy(LAYOUTCAIRO);
|
||||
cairo_destroy(CAIRO);
|
||||
cairo_surface_destroy(CAIROSURFACE);
|
||||
}
|
||||
|
||||
|
||||
void CHyprEasyLabel::draw(CMonitor* pMonitor, float a) {
|
||||
if (!g_pCompositor->windowValidMapped(m_pWindow))
|
||||
return;
|
||||
|
||||
if (!m_pWindow->m_sSpecialRenderData.decorate)
|
||||
return;
|
||||
|
||||
const auto PWORKSPACE = m_pWindow->m_pWorkspace;
|
||||
const auto WORKSPACEOFFSET = PWORKSPACE && !m_pWindow->m_bPinned ? PWORKSPACE->m_vRenderOffset.value() : Vector2D();
|
||||
|
||||
const auto ROUNDING = m_iRounding;
|
||||
|
||||
const auto scaledRounding = ROUNDING > 0 ? ROUNDING * pMonitor->scale : 0;
|
||||
|
||||
const auto DECOBOX = assignedBoxGlobal();
|
||||
|
||||
const auto BARBUF = DECOBOX.size() * pMonitor->scale;
|
||||
|
||||
//CBox motionBox = {DECOBOX.x - pMonitor->vecPosition.x, DECOBOX.y - pMonitor->vecPosition.y, DECOBOX.w,
|
||||
|
||||
auto TEXTBUF = DECOBOX.size() * pMonitor->scale;
|
||||
|
||||
|
||||
if (m_tTextTex.m_iTexID == 0) {
|
||||
renderMotionString(TEXTBUF, pMonitor->scale);
|
||||
}
|
||||
CBox motionBox = {DECOBOX.x, DECOBOX.y, layoutWidth, layoutHeight};
|
||||
motionBox.translate(pMonitor->vecPosition*-1).scale(pMonitor->scale).round();
|
||||
|
||||
if (motionBox.w < 1 || motionBox.h < 1)
|
||||
return;
|
||||
g_pHyprOpenGL->scissor(&motionBox);
|
||||
g_pHyprOpenGL->renderRect(&motionBox, m_cBackgroundColor, scaledRounding);
|
||||
|
||||
if (m_iBorderSize) {
|
||||
CBox borderBox = {DECOBOX.x, DECOBOX.y, layoutWidth, layoutHeight};
|
||||
borderBox.translate(pMonitor->vecPosition*-1).scale(pMonitor->scale).round();
|
||||
if (borderBox.w >= 1 && borderBox.h >= 1) {
|
||||
g_pHyprOpenGL->renderBorder(&borderBox, m_cBorderGradient, scaledRounding, m_iBorderSize * pMonitor->scale, a);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
g_pHyprOpenGL->renderTexture(m_tTextTex, &motionBox, a);
|
||||
|
||||
g_pHyprOpenGL->scissor((CBox*)nullptr);
|
||||
}
|
||||
|
||||
eDecorationType CHyprEasyLabel::getDecorationType() {
|
||||
return DECORATION_CUSTOM;
|
||||
}
|
||||
|
||||
void CHyprEasyLabel::updateWindow(CWindow* pWindow) {
|
||||
damageEntire();
|
||||
}
|
||||
|
||||
void CHyprEasyLabel::damageEntire() {
|
||||
; // ignored
|
||||
}
|
||||
|
||||
eDecorationLayer CHyprEasyLabel::getDecorationLayer() {
|
||||
return DECORATION_LAYER_OVERLAY;
|
||||
}
|
||||
|
||||
uint64_t CHyprEasyLabel::getDecorationFlags() {
|
||||
return DECORATION_PART_OF_MAIN_WINDOW;
|
||||
}
|
||||
|
||||
CBox CHyprEasyLabel::assignedBoxGlobal() {
|
||||
|
||||
double boxHeight, boxWidth;
|
||||
double boxSize;
|
||||
boxHeight = m_pWindow->m_vRealSize.value().y * 0.10;
|
||||
boxWidth = m_pWindow->m_vRealSize.value().x * 0.10;
|
||||
boxSize = std::min(boxHeight, boxWidth);
|
||||
double boxX = m_pWindow->m_vRealPosition.value().x + (m_pWindow->m_vRealSize.value().x-boxSize)/2;
|
||||
double boxY = m_pWindow->m_vRealPosition.value().y + (m_pWindow->m_vRealSize.value().y-boxSize)/2;
|
||||
CBox box = {boxX, boxY, boxSize, boxSize};
|
||||
|
||||
const auto PWORKSPACE = m_pWindow->m_pWorkspace;
|
||||
const auto WORKSPACEOFFSET = PWORKSPACE && !m_pWindow->m_bPinned ? PWORKSPACE->m_vRenderOffset.value() : Vector2D();
|
||||
|
||||
return box.translate(WORKSPACEOFFSET);
|
||||
}
|
||||
|
||||
CWindow* CHyprEasyLabel::getOwner() {
|
||||
return m_pWindow;
|
||||
}
|
||||
73
easymotionDeco.hpp
Normal file
73
easymotionDeco.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <hyprland/src/config/ConfigDataValues.hpp>
|
||||
#include <string>
|
||||
|
||||
#include <hyprland/src/render/decorations/IHyprWindowDecoration.hpp>
|
||||
#include <hyprland/src/render/OpenGL.hpp>
|
||||
#include "globals.hpp"
|
||||
|
||||
class CHyprEasyLabel : public IHyprWindowDecoration {
|
||||
public:
|
||||
CHyprEasyLabel(CWindow*, SMotionActionDesc *actionDesc);
|
||||
virtual ~CHyprEasyLabel();
|
||||
|
||||
virtual SDecorationPositioningInfo getPositioningInfo();
|
||||
|
||||
virtual void onPositioningReply(const SDecorationPositioningReply& reply);
|
||||
|
||||
virtual void draw(CMonitor*, float a);
|
||||
|
||||
virtual eDecorationType getDecorationType();
|
||||
|
||||
virtual void updateWindow(CWindow*);
|
||||
|
||||
virtual void damageEntire();
|
||||
|
||||
virtual eDecorationLayer getDecorationLayer();
|
||||
|
||||
virtual uint64_t getDecorationFlags();
|
||||
|
||||
bool m_bButtonsDirty = true;
|
||||
|
||||
virtual std::string getDisplayName();
|
||||
|
||||
CWindow* getOwner();
|
||||
|
||||
std::string m_szLabel;
|
||||
std::string m_szActionCmd;
|
||||
std::string m_szTextFont;
|
||||
int m_iTextSize;
|
||||
int m_iPaddingTop;
|
||||
int m_iPaddingBottom;
|
||||
int m_iPaddingLeft;
|
||||
int m_iPaddingRight;
|
||||
int m_iRounding;
|
||||
|
||||
CColor m_cTextColor;
|
||||
CColor m_cBackgroundColor;
|
||||
int m_iBorderSize;
|
||||
CGradientValueData m_cBorderGradient;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
int layoutWidth;
|
||||
int layoutHeight;
|
||||
SWindowDecorationExtents m_seExtents;
|
||||
|
||||
CWindow* m_pWindow = nullptr;
|
||||
|
||||
CTexture m_tTextTex;
|
||||
|
||||
bool m_bWindowSizeChanged = false;
|
||||
|
||||
void renderText(CTexture& out, const std::string& text, const CColor& color, const Vector2D& bufferSize, const float scale, const int fontSize);
|
||||
CBox assignedBoxGlobal();
|
||||
void renderMotionString(Vector2D& bufferSize, const float scale);
|
||||
|
||||
// for dynamic updates
|
||||
int m_iLastHeight = 0;
|
||||
};
|
||||
28
globals.hpp
Normal file
28
globals.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include <list>
|
||||
|
||||
#include <hyprland/src/plugins/PluginAPI.hpp>
|
||||
|
||||
inline HANDLE PHANDLE = nullptr;
|
||||
|
||||
class CHyprEasyLabel;
|
||||
|
||||
struct SGlobalState {
|
||||
std::vector<CHyprEasyLabel*> motionLabels;
|
||||
};
|
||||
|
||||
struct SMotionActionDesc {
|
||||
int textSize = 15;
|
||||
CColor textColor = CColor(0,0,0,1);
|
||||
CColor backgroundColor = CColor(1,1,1,1);
|
||||
std::string textFont = "Sans";
|
||||
std::string commandString = "";
|
||||
CCssGapData boxPadding = CCssGapData();
|
||||
int borderSize = 0;
|
||||
CGradientValueData borderColor = CGradientValueData();
|
||||
int rounding = 0;
|
||||
std::string motionKeys = "abcdefghijklmnopqrstuvwxyz1234567890";
|
||||
};
|
||||
|
||||
inline std::unique_ptr<SGlobalState> g_pGlobalState;
|
||||
|
||||
239
main.cpp
Normal file
239
main.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
#include <hyprland/src/plugins/PluginAPI.hpp>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <any>
|
||||
#include <ranges>
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/desktop/Window.hpp>
|
||||
#include <hyprland/src/config/ConfigManager.hpp>
|
||||
|
||||
#include "easymotionDeco.hpp"
|
||||
#include "globals.hpp"
|
||||
|
||||
// Do NOT change this function.
|
||||
APICALL EXPORT std::string PLUGIN_API_VERSION() {
|
||||
return HYPRLAND_API_VERSION;
|
||||
}
|
||||
|
||||
|
||||
void easymotionExitDispatch(std::string args)
|
||||
{
|
||||
for (auto &ml : g_pGlobalState->motionLabels | std::ranges::views::reverse) {
|
||||
ml->getOwner()->removeWindowDeco(ml);
|
||||
}
|
||||
HyprlandAPI::invokeHyprctlCommand("dispatch", "submap reset");
|
||||
|
||||
}
|
||||
|
||||
void easymotionActionDispatch(std::string args)
|
||||
{
|
||||
for (auto &ml : g_pGlobalState->motionLabels) {
|
||||
if (ml->m_szLabel == args) {
|
||||
g_pKeybindManager->m_mDispatchers["exec"](ml->m_szActionCmd);
|
||||
easymotionExitDispatch("");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addEasyMotionKeybinds()
|
||||
{
|
||||
|
||||
g_pKeybindManager->addKeybind(SKeybind{"escape", 0, 0, 0, "easymotionexit", "", 0, "__easymotionsubmap__", 0, 0, 0, 0, 0, 0});
|
||||
|
||||
//catchall
|
||||
g_pKeybindManager->addKeybind(SKeybind{"", 0, 1, 0, "", "", 0, "__easymotionsubmap__", 0, 0, 0, 0, 0, 0});
|
||||
|
||||
}
|
||||
|
||||
|
||||
void addLabelToWindow(CWindow *window, SMotionActionDesc *actionDesc, std::string &label)
|
||||
{
|
||||
std::unique_ptr<CHyprEasyLabel> motionlabel = std::make_unique<CHyprEasyLabel>(window, actionDesc);
|
||||
motionlabel.get()->m_szLabel = label;
|
||||
g_pGlobalState->motionLabels.push_back(motionlabel.get());
|
||||
HyprlandAPI::addWindowDecoration(PHANDLE, window, std::move(motionlabel));
|
||||
}
|
||||
|
||||
static bool parseBorderGradient(std::string VALUE, CGradientValueData *DATA) {
|
||||
std::string V = VALUE;
|
||||
|
||||
CVarList varlist(V, 0, ' ');
|
||||
DATA->m_vColors.clear();
|
||||
|
||||
std::string parseError = "";
|
||||
|
||||
for (auto& var : varlist) {
|
||||
if (var.find("deg") != std::string::npos) {
|
||||
// last arg
|
||||
try {
|
||||
DATA->m_fAngle = std::stoi(var.substr(0, var.find("deg"))) * (PI / 180.0); // radians
|
||||
} catch (...) {
|
||||
Debug::log(WARN, "Error parsing gradient {}", V);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (DATA->m_vColors.size() >= 10) {
|
||||
Debug::log(WARN, "Error parsing gradient {}: max colors is 10.", V);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
DATA->m_vColors.push_back(CColor(configStringToInt(var)));
|
||||
} catch (std::exception& e) {
|
||||
Debug::log(WARN, "Error parsing gradient {}", V);
|
||||
}
|
||||
}
|
||||
|
||||
if (DATA->m_vColors.size() == 0) {
|
||||
Debug::log(WARN, "Error parsing gradient {}", V);
|
||||
DATA->m_vColors.push_back(0); // transparent
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
void easymotionDispatch(std::string args)
|
||||
{
|
||||
|
||||
|
||||
static auto *const TEXTSIZE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:textsize")->getDataStaticPtr();
|
||||
|
||||
static auto *const TEXTCOLOR = (Hyprlang::INT* const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:textcolor")->getDataStaticPtr();
|
||||
static auto *const BGCOLOR = (Hyprlang::INT* const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:bgcolor")->getDataStaticPtr();
|
||||
static auto *const TEXTFONT = (Hyprlang::STRING const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:textfont")->getDataStaticPtr();
|
||||
static auto *const TEXTPADDING = (Hyprlang::STRING const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:textpadding")->getDataStaticPtr();
|
||||
static auto *const BORDERSIZE = (Hyprlang::INT* const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:bordersize")->getDataStaticPtr();
|
||||
static auto *const BORDERCOLOR = (Hyprlang::STRING const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:bordercolor")->getDataStaticPtr();
|
||||
static auto *const ROUNDING = (Hyprlang::INT* const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:rounding")->getDataStaticPtr();
|
||||
static auto *const MOTIONKEYS = (Hyprlang::STRING const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:motionkeys")->getDataStaticPtr();
|
||||
|
||||
CVarList emargs(args, 0, ';');
|
||||
SMotionActionDesc actionDesc;
|
||||
|
||||
actionDesc.textSize = **TEXTSIZE;
|
||||
actionDesc.textColor = **TEXTCOLOR;
|
||||
actionDesc.backgroundColor = **BGCOLOR;
|
||||
actionDesc.textFont = *TEXTFONT;
|
||||
CVarList cpadding = CVarList(*TEXTPADDING);
|
||||
actionDesc.boxPadding.parseGapData(cpadding);
|
||||
actionDesc.rounding = **ROUNDING;
|
||||
actionDesc.borderSize = **BORDERSIZE;
|
||||
if(!parseBorderGradient(*BORDERCOLOR, &actionDesc.borderColor)) {
|
||||
actionDesc.borderColor.m_vColors.clear();
|
||||
actionDesc.borderColor.m_fAngle = 0;
|
||||
}
|
||||
actionDesc.motionKeys = *MOTIONKEYS;
|
||||
|
||||
|
||||
for(int i = 0; i < emargs.size(); i++)
|
||||
{
|
||||
|
||||
CVarList kv(emargs[i], 2, ':');
|
||||
if (kv[0] == "action") {
|
||||
actionDesc.commandString = kv[1];
|
||||
} else if (kv[0] == "textsize") {
|
||||
actionDesc.textSize = configStringToInt(kv[1]);
|
||||
} else if (kv[0] == "textcolor") {
|
||||
actionDesc.textColor = CColor(configStringToInt(kv[1]));
|
||||
} else if (kv[0] == "bgcolor") {
|
||||
actionDesc.backgroundColor = CColor(configStringToInt(kv[1]));
|
||||
} else if (kv[0] == "textfont") {
|
||||
actionDesc.textFont = kv[1];
|
||||
} else if (kv[0] == "textpadding") {
|
||||
CVarList padVars = CVarList(kv[1]);
|
||||
actionDesc.boxPadding.parseGapData(padVars);
|
||||
} else if (kv[0] == "rounding") {
|
||||
actionDesc.rounding = configStringToInt(kv[1]);
|
||||
} else if (kv[0] == "bordersize") {
|
||||
actionDesc.borderSize = configStringToInt(kv[1]);
|
||||
} else if (kv[0] == "bordercolor") {
|
||||
CVarList varlist(kv[1], 0, ' ');
|
||||
actionDesc.borderColor.m_vColors.clear();
|
||||
actionDesc.borderColor.m_fAngle = 0;
|
||||
if(!parseBorderGradient(kv[1], &actionDesc.borderColor)) {
|
||||
actionDesc.borderColor.m_vColors.clear();
|
||||
actionDesc.borderColor.m_fAngle = 0;
|
||||
}
|
||||
} else if (kv[0] == "motionkeys") {
|
||||
actionDesc.motionKeys = kv[1];
|
||||
}
|
||||
}
|
||||
|
||||
int key_idx = 0;
|
||||
|
||||
for (auto &w : g_pCompositor->m_vWindows) {
|
||||
for (auto &m : g_pCompositor->m_vMonitors) {
|
||||
if (w->m_pWorkspace == m->activeWorkspace) {
|
||||
std::string lstr = actionDesc.motionKeys.substr(key_idx++, 1);
|
||||
addLabelToWindow(w.get(), &actionDesc, lstr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HyprlandAPI::invokeHyprctlCommand("dispatch", "submap __easymotionsubmap__");
|
||||
}
|
||||
|
||||
bool oneasymotionKeypress(void *self, std::any data) {
|
||||
|
||||
if (g_pGlobalState->motionLabels.empty()) return false;
|
||||
|
||||
auto map = std::any_cast<std::unordered_map<std::string, std::any>>(data);
|
||||
std::any kany = map["keyboard"];
|
||||
wlr_keyboard_key_event *ev = std::any_cast<wlr_keyboard_key_event *>(map["event"]);
|
||||
SKeyboard *keyboard = std::any_cast<SKeyboard *>(kany);
|
||||
|
||||
const auto KEYCODE = ev->keycode + 8;
|
||||
|
||||
const xkb_keysym_t KEYSYM = xkb_state_key_get_one_sym(keyboard->xkbTranslationState, KEYCODE);
|
||||
|
||||
if (ev->state != WL_KEYBOARD_KEY_STATE_PRESSED) return false;
|
||||
|
||||
xkb_keysym_t actionKeysym = 0;
|
||||
for (auto &ml : g_pGlobalState->motionLabels) {
|
||||
if (ml->m_szLabel != "") {
|
||||
actionKeysym = xkb_keysym_from_name(ml->m_szLabel.c_str(), XKB_KEYSYM_NO_FLAGS);
|
||||
if (actionKeysym && (actionKeysym == KEYSYM)) {
|
||||
easymotionActionDispatch(ml->m_szLabel);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||
PHANDLE = handle;
|
||||
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:textsize", Hyprlang::INT{15});
|
||||
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:textcolor", Hyprlang::INT{configStringToInt("rgba(ffffffff)")});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:bgcolor", Hyprlang::INT{configStringToInt("rgba(000000ff)")});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:textfont", Hyprlang::STRING{"Sans"});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:textpadding", Hyprlang::STRING{"0"});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:bordersize", Hyprlang::INT{0});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:bordercolor", Hyprlang::STRING{"rgba(ffffffff)"});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:rounding", Hyprlang::INT{0});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:motionkeys", Hyprlang::STRING{"abcdefghijklmnopqrstuvwxyz1234567890"});
|
||||
|
||||
|
||||
g_pGlobalState = std::make_unique<SGlobalState>();
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "easymotion", easymotionDispatch);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "easymotionaction", easymotionActionDispatch);
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "easymotionexit", easymotionExitDispatch);
|
||||
HyprlandAPI::registerCallbackDynamic(PHANDLE, "keyPress", [&](void *self, SCallbackInfo &info, std::any data) {
|
||||
info.cancelled = oneasymotionKeypress(self, data);
|
||||
});
|
||||
HyprlandAPI::registerCallbackDynamic(PHANDLE, "configReloaded", [&](void *self, SCallbackInfo&, std::any data) {addEasyMotionKeybinds();});
|
||||
HyprlandAPI::reloadConfig();
|
||||
|
||||
|
||||
return {"hypreasymotion", "Easymotion navigation", "Zakk", "1.0"};
|
||||
}
|
||||
|
||||
APICALL EXPORT void PLUGIN_EXIT() {
|
||||
}
|
||||
Reference in New Issue
Block a user