diff --git a/assets/example.conf b/assets/example.conf index f815bfc..20a909a 100644 --- a/assets/example.conf +++ b/assets/example.conf @@ -9,7 +9,7 @@ $font = Monospace general { - hide_cursor = true + hide_cursor = false } # uncomment to enable fingerprint authentication @@ -90,3 +90,14 @@ label { halign = right valign = top } + +label { + monitor = + text = $LAYOUT[en,ru] + font_size = 24 + onclick = hyprctl switchxkblayout all next + + position = 250, -20 + halign = center + valign = center +} diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 1e2cb7f..20d4941 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -209,6 +209,9 @@ void CConfigManager::init() { m_config.addSpecialConfigValue(name, "shadow_passes", Hyprlang::INT{0}); \ m_config.addSpecialConfigValue(name, "shadow_color", Hyprlang::INT{0xFF000000}); \ m_config.addSpecialConfigValue(name, "shadow_boost", Hyprlang::FLOAT{1.2}); + +#define CLICKABLE(name) m_config.addSpecialConfigValue(name, "onclick", Hyprlang::STRING{""}); + m_config.addConfigValue("general:text_trim", Hyprlang::INT{1}); m_config.addConfigValue("general:hide_cursor", Hyprlang::INT{0}); m_config.addConfigValue("general:grace", Hyprlang::INT{0}); @@ -257,6 +260,7 @@ void CConfigManager::init() { m_config.addSpecialConfigValue("shape", "xray", Hyprlang::INT{0}); m_config.addSpecialConfigValue("shape", "zindex", Hyprlang::INT{0}); SHADOWABLE("shape"); + CLICKABLE("shape"); m_config.addSpecialCategory("image", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); m_config.addSpecialConfigValue("image", "monitor", Hyprlang::STRING{""}); @@ -273,6 +277,7 @@ void CConfigManager::init() { m_config.addSpecialConfigValue("image", "reload_cmd", Hyprlang::STRING{""}); m_config.addSpecialConfigValue("image", "zindex", Hyprlang::INT{0}); SHADOWABLE("image"); + CLICKABLE("image"); m_config.addSpecialCategory("input-field", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); m_config.addSpecialConfigValue("input-field", "monitor", Hyprlang::STRING{""}); @@ -320,6 +325,7 @@ void CConfigManager::init() { m_config.addSpecialConfigValue("label", "text_align", Hyprlang::STRING{""}); m_config.addSpecialConfigValue("label", "zindex", Hyprlang::INT{0}); SHADOWABLE("label"); + CLICKABLE("label"); m_config.registerHandler(&::handleSource, "source", {.allowFlags = false}); m_config.registerHandler(&::handleBezier, "bezier", {.allowFlags = false}); @@ -356,6 +362,7 @@ void CConfigManager::init() { Debug::log(ERR, "Config has errors:\n{}\nProceeding ignoring faulty entries", result.getError()); #undef SHADOWABLE +#undef CLICKABLE } std::vector CConfigManager::getWidgetConfigs() { @@ -367,6 +374,8 @@ std::vector CConfigManager::getWidgetConfigs() { "shadow_boost", m_config.getSpecialConfigValue(name, "shadow_boost", k.c_str()) \ } +#define CLICKABLE(name) {"onclick", m_config.getSpecialConfigValue(name, "onclick", k.c_str())} + // auto keys = m_config.listKeysForSpecialCategory("background"); result.reserve(keys.size()); @@ -414,6 +423,7 @@ std::vector CConfigManager::getWidgetConfigs() { {"xray", m_config.getSpecialConfigValue("shape", "xray", k.c_str())}, {"zindex", m_config.getSpecialConfigValue("shape", "zindex", k.c_str())}, SHADOWABLE("shape"), + CLICKABLE("shape"), } }); // clang-format on @@ -440,6 +450,7 @@ std::vector CConfigManager::getWidgetConfigs() { {"reload_cmd", m_config.getSpecialConfigValue("image", "reload_cmd", k.c_str())}, {"zindex", m_config.getSpecialConfigValue("image", "zindex", k.c_str())}, SHADOWABLE("image"), + CLICKABLE("image"), } }); // clang-format on @@ -505,6 +516,7 @@ std::vector CConfigManager::getWidgetConfigs() { {"text_align", m_config.getSpecialConfigValue("label", "text_align", k.c_str())}, {"zindex", m_config.getSpecialConfigValue("label", "zindex", k.c_str())}, SHADOWABLE("label"), + CLICKABLE("label"), } }); // clang-format on diff --git a/src/core/CursorShape.cpp b/src/core/CursorShape.cpp index dd3201c..6712720 100644 --- a/src/core/CursorShape.cpp +++ b/src/core/CursorShape.cpp @@ -9,12 +9,15 @@ CCursorShape::CCursorShape(SP mgr) : mgr(mgr) { } void CCursorShape::setShape(const wpCursorShapeDeviceV1Shape shape) { - if (!dev) + if (!g_pSeatManager->m_pPointer) return; + if (!dev) + dev = makeShared(mgr->sendGetPointer(g_pSeatManager->m_pPointer->resource())); + dev->sendSetShape(lastCursorSerial, shape); } void CCursorShape::hideCursor() { g_pSeatManager->m_pPointer->sendSetCursor(lastCursorSerial, nullptr, 0, 0); -} \ No newline at end of file +} diff --git a/src/core/LockSurface.cpp b/src/core/LockSurface.cpp index 8bed076..762549e 100644 --- a/src/core/LockSurface.cpp +++ b/src/core/LockSurface.cpp @@ -139,3 +139,7 @@ void CSessionLockSurface::onCallback() { render(); } } + +SP CSessionLockSurface::getWlSurface() { + return surface; +} diff --git a/src/core/LockSurface.hpp b/src/core/LockSurface.hpp index 15a3827..fdbac6b 100644 --- a/src/core/LockSurface.hpp +++ b/src/core/LockSurface.hpp @@ -17,15 +17,16 @@ class CSessionLockSurface { CSessionLockSurface(const SP& pOutput); ~CSessionLockSurface(); - void configure(const Vector2D& size, uint32_t serial); + void configure(const Vector2D& size, uint32_t serial); - bool readyForFrame = false; + bool readyForFrame = false; - float fractionalScale = 1.0; + float fractionalScale = 1.0; - void render(); - void onCallback(); - void onScaleUpdate(); + void render(); + void onCallback(); + void onScaleUpdate(); + SP getWlSurface(); private: WP m_outputRef; diff --git a/src/core/Seat.cpp b/src/core/Seat.cpp index 56d96fd..408e64d 100644 --- a/src/core/Seat.cpp +++ b/src/core/Seat.cpp @@ -26,7 +26,13 @@ void CSeatManager::registerSeat(SP seat) { if (caps & WL_SEAT_CAPABILITY_POINTER) { m_pPointer = makeShared(r->sendGetPointer()); + static const auto HIDECURSOR = g_pConfigManager->getValue("general:hide_cursor"); m_pPointer->setMotion([](CCWlPointer* r, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { + g_pHyprlock->m_vMouseLocation = {wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)}; + + if (!*HIDECURSOR) + g_pHyprlock->onHover(g_pHyprlock->m_vMouseLocation); + if (std::chrono::system_clock::now() > g_pHyprlock->m_tGraceEnds) return; @@ -40,16 +46,34 @@ void CSeatManager::registerSeat(SP seat) { if (!m_pCursorShape) return; - static const auto HIDE = g_pConfigManager->getValue("general:hide_cursor"); - m_pCursorShape->lastCursorSerial = serial; - if (*HIDE) + if (*HIDECURSOR) m_pCursorShape->hideCursor(); else m_pCursorShape->setShape(wpCursorShapeDeviceV1Shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); g_pHyprlock->m_vLastEnterCoords = {wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)}; + + if (*HIDECURSOR) + return; + + for (const auto& POUTPUT : g_pHyprlock->m_vOutputs) { + if (!POUTPUT->m_sessionLockSurface) + continue; + + const auto& PWLSURFACE = POUTPUT->m_sessionLockSurface->getWlSurface(); + if (PWLSURFACE->resource() == surf) + g_pHyprlock->m_focusedOutput = POUTPUT; + } + }); + + m_pPointer->setLeave([](CCWlPointer* r, uint32_t serial, wl_proxy* surf) { g_pHyprlock->m_focusedOutput.reset(); }); + + m_pPointer->setButton([](CCWlPointer* r, uint32_t serial, uint32_t time, uint32_t button, wl_pointer_button_state state) { + if (*HIDECURSOR) + return; + g_pHyprlock->onClick(button, state == WL_POINTER_BUTTON_STATE_PRESSED, g_pHyprlock->m_vMouseLocation); }); } diff --git a/src/core/hyprlock.cpp b/src/core/hyprlock.cpp index c59b561..b330262 100644 --- a/src/core/hyprlock.cpp +++ b/src/core/hyprlock.cpp @@ -669,6 +669,61 @@ void CHyprlock::handleKeySym(xkb_keysym_t sym, bool composed) { } } +void CHyprlock::onClick(uint32_t button, bool down, const Vector2D& pos) { + if (!m_focusedOutput.lock()) + return; + + // TODO: add the UNLIKELY marco from Hyprland + if (!m_focusedOutput->m_sessionLockSurface) + return; + + const auto SCALEDPOS = pos * m_focusedOutput->m_sessionLockSurface->fractionalScale; + const auto widgets = g_pRenderer->getOrCreateWidgetsFor(*m_focusedOutput->m_sessionLockSurface); + for (const auto& widget : widgets) { + if (widget->containsPoint(SCALEDPOS)) + widget->onClick(button, down, pos); + } +} + +void CHyprlock::onHover(const Vector2D& pos) { + if (!m_focusedOutput.lock()) + return; + + if (!m_focusedOutput->m_sessionLockSurface) + return; + + bool outputNeedsRedraw = false; + bool cursorChanged = false; + + const auto SCALEDPOS = pos * m_focusedOutput->m_sessionLockSurface->fractionalScale; + const auto widgets = g_pRenderer->getOrCreateWidgetsFor(*m_focusedOutput->m_sessionLockSurface); + for (const auto& widget : widgets) { + const bool CONTAINSPOINT = widget->containsPoint(SCALEDPOS); + const bool HOVERED = widget->isHovered(); + + if (CONTAINSPOINT) { + if (!HOVERED) { + widget->setHover(true); + widget->onHover(pos); + outputNeedsRedraw = true; + } + + if (!cursorChanged) + cursorChanged = true; + + } else if (HOVERED) { + widget->setHover(false); + outputNeedsRedraw = true; + } + } + + if (!cursorChanged) + g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); + + if (outputNeedsRedraw) + m_focusedOutput->m_sessionLockSurface->render(); +} + bool CHyprlock::acquireSessionLock() { Debug::log(LOG, "Locking session"); m_sLockState.lock = makeShared(m_sWaylandState.sessionLock->sendLock()); @@ -817,19 +872,6 @@ void CHyprlock::enqueueForceUpdateTimers() { nullptr, false); } -std::string CHyprlock::spawnSync(const std::string& cmd) { - CProcess proc("/bin/sh", {"-c", cmd}); - if (!proc.runSync()) { - Debug::log(ERR, "Failed to run \"{}\"", cmd); - return ""; - } - - if (!proc.stdErr().empty()) - Debug::log(ERR, "Shell command \"{}\" STDERR:\n{}", cmd, proc.stdErr()); - - return proc.stdOut(); -} - SP CHyprlock::getScreencopy() { return m_sWaylandState.screencopy; } diff --git a/src/core/hyprlock.hpp b/src/core/hyprlock.hpp index 64505ac..0d4f1cf 100644 --- a/src/core/hyprlock.hpp +++ b/src/core/hyprlock.hpp @@ -48,9 +48,9 @@ class CHyprlock { bool acquireSessionLock(); void releaseSessionLock(); - std::string spawnSync(const std::string& cmd); - void onKey(uint32_t key, bool down); + void onClick(uint32_t button, bool down, const Vector2D& pos); + void onHover(const Vector2D& pos); void startKeyRepeat(xkb_keysym_t sym); void repeatKey(xkb_keysym_t sym); void handleKeySym(xkb_keysym_t sym, bool compose); @@ -95,6 +95,9 @@ class CHyprlock { // std::chrono::system_clock::time_point m_tGraceEnds; Vector2D m_vLastEnterCoords = {}; + WP m_focusedOutput; + + Vector2D m_vMouseLocation = {}; std::shared_ptr m_pKeyRepeatTimer = nullptr; diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp index 1a9d3a1..33ac120 100644 --- a/src/helpers/MiscFunctions.cpp +++ b/src/helpers/MiscFunctions.cpp @@ -5,9 +5,11 @@ #include "MiscFunctions.hpp" #include "Log.hpp" #include +#include #include using namespace Hyprutils::String; +using namespace Hyprutils::OS; std::string absolutePath(const std::string& rawpath, const std::string& currentDir) { std::filesystem::path path(rawpath); @@ -137,3 +139,22 @@ int createPoolFile(size_t size, std::string& name) { return FD; } + +std::string spawnSync(const std::string& cmd) { + CProcess proc("/bin/sh", {"-c", cmd}); + if (!proc.runSync()) { + Debug::log(ERR, "Failed to run \"{}\"", cmd); + return ""; + } + + if (!proc.stdErr().empty()) + Debug::log(ERR, "Shell command \"{}\" STDERR:\n{}", cmd, proc.stdErr()); + + return proc.stdOut(); +} + +void spawnAsync(const std::string& cmd) { + CProcess proc("/bin/sh", {"-c", cmd}); + if (!proc.runAsync()) + Debug::log(ERR, "Failed to start \"{}\"", cmd); +} diff --git a/src/helpers/MiscFunctions.hpp b/src/helpers/MiscFunctions.hpp index c2fb126..6e7a259 100644 --- a/src/helpers/MiscFunctions.hpp +++ b/src/helpers/MiscFunctions.hpp @@ -7,3 +7,5 @@ std::string absolutePath(const std::string&, const std::string&); int64_t configStringToInt(const std::string& VALUE); int createPoolFile(size_t size, std::string& name); +std::string spawnSync(const std::string& cmd); +void spawnAsync(const std::string& cmd); diff --git a/src/renderer/AsyncResourceGatherer.cpp b/src/renderer/AsyncResourceGatherer.cpp index 45219c5..5355a3f 100644 --- a/src/renderer/AsyncResourceGatherer.cpp +++ b/src/renderer/AsyncResourceGatherer.cpp @@ -218,7 +218,7 @@ void CAsyncResourceGatherer::renderText(const SPreloadRequest& rq) { const bool ISCMD = rq.props.contains("cmd") ? std::any_cast(rq.props.at("cmd")) : false; static const auto TRIM = g_pConfigManager->getValue("general:text_trim"); - std::string text = ISCMD ? g_pHyprlock->spawnSync(rq.asset) : rq.asset; + std::string text = ISCMD ? spawnSync(rq.asset) : rq.asset; if (*TRIM) { text.erase(0, text.find_first_not_of(" \n\r\t")); diff --git a/src/renderer/Framebuffer.cpp b/src/renderer/Framebuffer.cpp index 7843860..714e8db 100644 --- a/src/renderer/Framebuffer.cpp +++ b/src/renderer/Framebuffer.cpp @@ -117,6 +117,6 @@ CFramebuffer::~CFramebuffer() { release(); } -bool CFramebuffer::isAllocated() { +bool CFramebuffer::isAllocated() const { return m_iFb != (GLuint)-1; -} \ No newline at end of file +} diff --git a/src/renderer/Framebuffer.hpp b/src/renderer/Framebuffer.hpp index 02a4fb1..d23ed50 100644 --- a/src/renderer/Framebuffer.hpp +++ b/src/renderer/Framebuffer.hpp @@ -13,7 +13,7 @@ class CFramebuffer { void bind() const; void release(); void reset(); - bool isAllocated(); + bool isAllocated() const; Vector2D m_vSize; @@ -21,4 +21,4 @@ class CFramebuffer { GLuint m_iFb = -1; CTexture* m_pStencilTex = nullptr; -}; \ No newline at end of file +}; diff --git a/src/renderer/Renderer.hpp b/src/renderer/Renderer.hpp index 9d32de5..91921f6 100644 --- a/src/renderer/Renderer.hpp +++ b/src/renderer/Renderer.hpp @@ -48,27 +48,26 @@ class CRenderer { void startFadeIn(); void startFadeOut(bool unlock = false, bool immediate = true); + std::vector>& getOrCreateWidgetsFor(const CSessionLockSurface& surf); private: - widgetMap_t widgets; + widgetMap_t widgets; - std::vector>& getOrCreateWidgetsFor(const CSessionLockSurface& surf); + CShader rectShader; + CShader texShader; + CShader texMixShader; + CShader blurShader1; + CShader blurShader2; + CShader blurPrepareShader; + CShader blurFinishShader; + CShader borderShader; - CShader rectShader; - CShader texShader; - CShader texMixShader; - CShader blurShader1; - CShader blurShader2; - CShader blurPrepareShader; - CShader blurFinishShader; - CShader borderShader; + Mat3x3 projMatrix = Mat3x3::identity(); + Mat3x3 projection; - Mat3x3 projMatrix = Mat3x3::identity(); - Mat3x3 projection; + PHLANIMVAR opacity; - PHLANIMVAR opacity; - - std::vector boundFBs; + std::vector boundFBs; }; inline UP g_pRenderer; diff --git a/src/renderer/widgets/Background.cpp b/src/renderer/widgets/Background.cpp index 67fb016..2971159 100644 --- a/src/renderer/widgets/Background.cpp +++ b/src/renderer/widgets/Background.cpp @@ -240,7 +240,7 @@ void CBackground::onReloadTimerUpdate() { // Path parsing and early returns if (!reloadCommand.empty()) { - path = g_pHyprlock->spawnSync(reloadCommand); + path = spawnSync(reloadCommand); if (path.ends_with('\n')) path.pop_back(); diff --git a/src/renderer/widgets/IWidget.cpp b/src/renderer/widgets/IWidget.cpp index a224bc6..dbfc733 100644 --- a/src/renderer/widgets/IWidget.cpp +++ b/src/renderer/widgets/IWidget.cpp @@ -266,3 +266,15 @@ IWidget::SFormatResult IWidget::formatString(std::string in) { result.formatted = in; return result; } + +void IWidget::setHover(bool hover) { + hovered = hover; +} + +bool IWidget::isHovered() const { + return hovered; +} + +bool IWidget::containsPoint(const Vector2D& pos) const { + return getBoundingBoxWl().containsPoint(pos); +} diff --git a/src/renderer/widgets/IWidget.hpp b/src/renderer/widgets/IWidget.hpp index f13f6c3..2ef45b3 100644 --- a/src/renderer/widgets/IWidget.hpp +++ b/src/renderer/widgets/IWidget.hpp @@ -1,7 +1,7 @@ #pragma once -#include "../../helpers/Math.hpp" #include "../../defines.hpp" +#include "../../helpers/Math.hpp" #include #include #include @@ -24,6 +24,13 @@ class IWidget { static int roundingForBox(const CBox& box, int roundingConfig); static int roundingForBorderBox(const CBox& borderBox, int roundingConfig, int thickness); + virtual CBox getBoundingBoxWl() const { + return CBox(); + }; + virtual void onClick(uint32_t button, bool down, const Vector2D& pos) {} + virtual void onHover(const Vector2D& pos) {} + bool containsPoint(const Vector2D& pos) const; + struct SFormatResult { std::string formatted; float updateEveryMs = 0; // 0 means don't (static) @@ -33,4 +40,10 @@ class IWidget { }; static SFormatResult formatString(std::string in); + + void setHover(bool hover); + bool isHovered() const; + + private: + bool hovered = false; }; diff --git a/src/renderer/widgets/Image.cpp b/src/renderer/widgets/Image.cpp index 9b19f6d..c777f53 100644 --- a/src/renderer/widgets/Image.cpp +++ b/src/renderer/widgets/Image.cpp @@ -6,6 +6,7 @@ #include "../../config/ConfigDataValues.hpp" #include #include +#include CImage::~CImage() { reset(); @@ -31,7 +32,7 @@ void CImage::onTimerUpdate() { const std::string OLDPATH = path; if (!reloadCommand.empty()) { - path = g_pHyprlock->spawnSync(reloadCommand); + path = spawnSync(reloadCommand); if (path.ends_with('\n')) path.pop_back(); @@ -84,18 +85,19 @@ void CImage::configure(const std::unordered_map& props, c shadow.configure(m_self.lock(), props, viewport); try { - size = std::any_cast(props.at("size")); - rounding = std::any_cast(props.at("rounding")); - border = std::any_cast(props.at("border_size")); - color = *CGradientValueData::fromAnyPv(props.at("border_color")); - pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport); - halign = std::any_cast(props.at("halign")); - valign = std::any_cast(props.at("valign")); - angle = std::any_cast(props.at("rotate")); + size = std::any_cast(props.at("size")); + rounding = std::any_cast(props.at("rounding")); + border = std::any_cast(props.at("border_size")); + color = *CGradientValueData::fromAnyPv(props.at("border_color")); + configPos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport); + halign = std::any_cast(props.at("halign")); + valign = std::any_cast(props.at("valign")); + angle = std::any_cast(props.at("rotate")); - path = std::any_cast(props.at("path")); - reloadTime = std::any_cast(props.at("reload_time")); - reloadCommand = std::any_cast(props.at("reload_cmd")); + path = std::any_cast(props.at("path")); + reloadTime = std::any_cast(props.at("reload_time")); + reloadCommand = std::any_cast(props.at("reload_cmd")); + onclickCommand = std::any_cast(props.at("onclick")); } catch (const std::bad_any_cast& e) { RASSERT(false, "Failed to construct CImage: {}", e.what()); // } catch (const std::out_of_range& e) { @@ -196,10 +198,10 @@ bool CImage::draw(const SRenderData& data) { shadow.draw(data); - const auto TEXPOS = posFromHVAlign(viewport, tex->m_vSize, pos, halign, valign, angle); + pos = posFromHVAlign(viewport, tex->m_vSize, configPos, halign, valign, angle); - texbox.x = TEXPOS.x; - texbox.y = TEXPOS.y; + texbox.x = pos.x; + texbox.y = pos.y; texbox.round(); texbox.rot = angle; @@ -233,3 +235,23 @@ void CImage::renderUpdate() { g_pHyprlock->renderOutput(stringPort); } + +CBox CImage::getBoundingBoxWl() const { + if (!imageFB.isAllocated()) + return CBox{}; + + return { + Vector2D{pos.x, viewport.y - pos.y - imageFB.m_cTex.m_vSize.y}, + imageFB.m_cTex.m_vSize, + }; +} + +void CImage::onClick(uint32_t button, bool down, const Vector2D& pos) { + if (down && !onclickCommand.empty()) + spawnAsync(onclickCommand); +} + +void CImage::onHover(const Vector2D& pos) { + if (!onclickCommand.empty()) + g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER); +} diff --git a/src/renderer/widgets/Image.hpp b/src/renderer/widgets/Image.hpp index 16e04b8..3afa3a7 100644 --- a/src/renderer/widgets/Image.hpp +++ b/src/renderer/widgets/Image.hpp @@ -24,6 +24,9 @@ class CImage : public IWidget { virtual void configure(const std::unordered_map& props, const SP& pOutput); virtual bool draw(const SRenderData& data); + virtual CBox getBoundingBoxWl() const; + virtual void onClick(uint32_t button, bool down, const Vector2D& pos); + virtual void onHover(const Vector2D& pos); void reset(); @@ -42,6 +45,7 @@ class CImage : public IWidget { double angle; CGradientValueData color; Vector2D pos; + Vector2D configPos; std::string halign, valign, path; @@ -49,6 +53,8 @@ class CImage : public IWidget { int reloadTime; std::string reloadCommand; + std::string onclickCommand; + std::filesystem::file_time_type modificationTime; std::shared_ptr imageTimer; CAsyncResourceGatherer::SPreloadRequest request; diff --git a/src/renderer/widgets/Label.cpp b/src/renderer/widgets/Label.cpp index aa57aba..7befb19 100644 --- a/src/renderer/widgets/Label.cpp +++ b/src/renderer/widgets/Label.cpp @@ -3,6 +3,7 @@ #include "../../helpers/Log.hpp" #include "../../core/hyprlock.hpp" #include "../../helpers/Color.hpp" +#include "../../helpers/MiscFunctions.hpp" #include "../../config/ConfigDataValues.hpp" #include #include @@ -79,6 +80,7 @@ void CLabel::configure(const std::unordered_map& props, c valign = std::any_cast(props.at("valign")); angle = std::any_cast(props.at("rotate")); angle = angle * M_PI / 180.0; + onclickCommand = std::any_cast(props.at("onclick")); std::string textAlign = std::any_cast(props.at("text_align")); std::string fontFamily = std::any_cast(props.at("font_family")); @@ -172,3 +174,23 @@ void CLabel::renderUpdate() { g_pHyprlock->renderOutput(outputStringPort); } + +CBox CLabel::getBoundingBoxWl() const { + if (!asset) + return CBox{}; + + return { + Vector2D{pos.x, viewport.y - pos.y - asset->texture.m_vSize.y}, + asset->texture.m_vSize, + }; +} + +void CLabel::onClick(uint32_t button, bool down, const Vector2D& pos) { + if (down && !onclickCommand.empty()) + spawnAsync(onclickCommand); +} + +void CLabel::onHover(const Vector2D& pos) { + if (!onclickCommand.empty()) + g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER); +} diff --git a/src/renderer/widgets/Label.hpp b/src/renderer/widgets/Label.hpp index b9d489a..b43a2c4 100644 --- a/src/renderer/widgets/Label.hpp +++ b/src/renderer/widgets/Label.hpp @@ -21,6 +21,9 @@ class CLabel : public IWidget { virtual void configure(const std::unordered_map& prop, const SP& pOutput); virtual bool draw(const SRenderData& data); + virtual CBox getBoundingBoxWl() const; + virtual void onClick(uint32_t button, bool down, const Vector2D& pos); + virtual void onHover(const Vector2D& pos); void reset(); @@ -43,6 +46,7 @@ class CLabel : public IWidget { std::string resourceID; std::string pendingResourceID; // if dynamic label std::string halign, valign; + std::string onclickCommand; SPreloadedAsset* asset = nullptr; std::string outputStringPort; diff --git a/src/renderer/widgets/PasswordInputField.cpp b/src/renderer/widgets/PasswordInputField.cpp index b27a0ee..8023d7a 100644 --- a/src/renderer/widgets/PasswordInputField.cpp +++ b/src/renderer/widgets/PasswordInputField.cpp @@ -473,3 +473,14 @@ void CPasswordInputField::updateColors() { colorState.font = fontTarget; } + +CBox CPasswordInputField::getBoundingBoxWl() const { + return { + Vector2D{pos.x, viewport.y - pos.y - size->value().y}, + size->value(), + }; +} + +void CPasswordInputField::onHover(const Vector2D& pos) { + g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT); +} diff --git a/src/renderer/widgets/PasswordInputField.hpp b/src/renderer/widgets/PasswordInputField.hpp index 179680d..764d83a 100644 --- a/src/renderer/widgets/PasswordInputField.hpp +++ b/src/renderer/widgets/PasswordInputField.hpp @@ -23,6 +23,8 @@ class CPasswordInputField : public IWidget { virtual void configure(const std::unordered_map& prop, const SP& pOutput); virtual bool draw(const SRenderData& data); + virtual void onHover(const Vector2D& pos); + virtual CBox getBoundingBoxWl() const; void reset(); void onFadeOutTimer(); diff --git a/src/renderer/widgets/Shape.cpp b/src/renderer/widgets/Shape.cpp index 6499245..cc3e73b 100644 --- a/src/renderer/widgets/Shape.cpp +++ b/src/renderer/widgets/Shape.cpp @@ -1,8 +1,11 @@ #include "Shape.hpp" #include "../Renderer.hpp" #include "../../config/ConfigDataValues.hpp" +#include "../../core/hyprlock.hpp" +#include "../../helpers/MiscFunctions.hpp" #include #include +#include void CShape::registerSelf(const SP& self) { m_self = self; @@ -14,16 +17,17 @@ void CShape::configure(const std::unordered_map& props, c shadow.configure(m_self.lock(), props, viewport); try { - size = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport); - rounding = std::any_cast(props.at("rounding")); - border = std::any_cast(props.at("border_size")); - color = std::any_cast(props.at("color")); - borderGrad = *CGradientValueData::fromAnyPv(props.at("border_color")); - pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport); - halign = std::any_cast(props.at("halign")); - valign = std::any_cast(props.at("valign")); - angle = std::any_cast(props.at("rotate")); - xray = std::any_cast(props.at("xray")); + size = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport); + rounding = std::any_cast(props.at("rounding")); + border = std::any_cast(props.at("border_size")); + color = std::any_cast(props.at("color")); + borderGrad = *CGradientValueData::fromAnyPv(props.at("border_color")); + pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport); + halign = std::any_cast(props.at("halign")); + valign = std::any_cast(props.at("valign")); + angle = std::any_cast(props.at("rotate")); + xray = std::any_cast(props.at("xray")); + onclickCommand = std::any_cast(props.at("onclick")); } catch (const std::bad_any_cast& e) { RASSERT(false, "Failed to construct CShape: {}", e.what()); // } catch (const std::out_of_range& e) { @@ -100,3 +104,19 @@ bool CShape::draw(const SRenderData& data) { return data.opacity < 1.0; } +CBox CShape::getBoundingBoxWl() const { + return { + Vector2D{pos.x, viewport.y - pos.y - size.y}, + size, + }; +} + +void CShape::onClick(uint32_t button, bool down, const Vector2D& pos) { + if (down && !onclickCommand.empty()) + spawnAsync(onclickCommand); +} + +void CShape::onHover(const Vector2D& pos) { + if (!onclickCommand.empty()) + g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER); +} diff --git a/src/renderer/widgets/Shape.hpp b/src/renderer/widgets/Shape.hpp index 25c43a0..133a9ae 100644 --- a/src/renderer/widgets/Shape.hpp +++ b/src/renderer/widgets/Shape.hpp @@ -18,6 +18,9 @@ class CShape : public IWidget { virtual void configure(const std::unordered_map& prop, const SP& pOutput); virtual bool draw(const SRenderData& data); + virtual CBox getBoundingBoxWl() const; + virtual void onClick(uint32_t button, bool down, const Vector2D& pos); + virtual void onHover(const Vector2D& pos); private: WP m_self; @@ -38,6 +41,7 @@ class CShape : public IWidget { std::string halign, valign; bool firstRender = true; + std::string onclickCommand; Vector2D viewport; CShadowable shadow;