From 3e38762e84d907b3ccb09353aedd1113922e705d Mon Sep 17 00:00:00 2001 From: bvr-yr <130279855+bvr-yr@users.noreply.github.com> Date: Sat, 16 Mar 2024 17:44:44 +0300 Subject: [PATCH] widgets: add Image (#191) --- nix/hm-module.nix | 87 ++++++++++++++++++++++++++ src/config/ConfigManager.cpp | 34 ++++++++++ src/renderer/AsyncResourceGatherer.cpp | 6 +- src/renderer/Renderer.cpp | 9 +++ src/renderer/widgets/Image.cpp | 68 ++++++++++++++++++++ src/renderer/widgets/Image.hpp | 36 +++++++++++ 6 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 src/renderer/widgets/Image.cpp create mode 100644 src/renderer/widgets/Image.hpp diff --git a/nix/hm-module.nix b/nix/hm-module.nix index 36b5c85..c1e3c1f 100644 --- a/nix/hm-module.nix +++ b/nix/hm-module.nix @@ -162,6 +162,77 @@ in { ]; }; + images = mkOption { + description = "Image configurations"; + type = listOf (submodule { + options = { + monitor = mkOption { + description = "The monitor to draw an image"; + type = str; + default = ""; + }; + + path = mkOption { + description = "The path to source image"; + type = str; + default = "/home/me/cutie.png"; # only png supported for now + }; + + size = mkOption { + description = "Size of the image. Lesser side is chosen if not 1:1 aspect ratio"; + type = int; + default = 150; + }; + + rounding = mkOption { + description = "The rounding of the image"; + type = int; + default = -1; + }; + + border_size = mkOption { + description = "Size of image border"; + type = int; + default = 4; + }; + + border_color = mkOption { + description = "Color of image border"; + type = str; + default = "rgb(221, 221, 221)"; + }; + + position = { + x = mkOption { + description = "X position of the image"; + type = int; + default = 0; + }; + y = mkOption { + description = "Y position of the image"; + type = int; + default = 200; + }; + }; + + halign = mkOption { + description = "Horizontal alignment of the image"; + type = str; + default = "center"; + }; + + valign = mkOption { + description = "Vertical alignment of the image"; + type = str; + default = "center"; + }; + }; + }); + default = [ + {} + ]; + }; + input-fields = mkOption { description = "Input field configurations"; type = listOf (submodule { @@ -441,6 +512,22 @@ in { '') cfg.backgrounds)} + ${builtins.concatStringsSep "\n" (map (image: '' + image { + monitor = ${image.monitor} + path = ${image.path} + size = ${toString image.size} + rounding = ${toString image.rounding} + border_size = ${toString image.border_size} + border_color = ${image.border_color} + + position = ${toString image.position.x}, ${toString image.position.y} + halign = ${image.halign} + valign = ${image.valign} + } + '') + cfg.images)} + ${builtins.concatStringsSep "\n" (map (input-field: '' input-field { monitor = ${input-field.monitor} diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 572f535..a90b7b7 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -61,6 +61,18 @@ void CConfigManager::init() { m_config.addSpecialConfigValue("background", "vibrancy", Hyprlang::FLOAT{0.1686}); m_config.addSpecialConfigValue("background", "vibrancy_darkness", Hyprlang::FLOAT{0.05}); + m_config.addSpecialCategory("image", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); + m_config.addSpecialConfigValue("image", "monitor", Hyprlang::STRING{""}); + m_config.addSpecialConfigValue("image", "path", Hyprlang::STRING{""}); + m_config.addSpecialConfigValue("image", "size", Hyprlang::INT{150}); + m_config.addSpecialConfigValue("image", "rounding", Hyprlang::INT{-1}); + m_config.addSpecialConfigValue("image", "border_size", Hyprlang::INT{4}); + m_config.addSpecialConfigValue("image", "border_color", Hyprlang::INT{0xFFDDDDDD}); + m_config.addSpecialConfigValue("image", "position", Hyprlang::VEC2{0, 200}); + m_config.addSpecialConfigValue("image", "halign", Hyprlang::STRING{"center"}); + m_config.addSpecialConfigValue("image", "valign", Hyprlang::STRING{"center"}); + SHADOWABLE("image"); + m_config.addSpecialCategory("input-field", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); m_config.addSpecialConfigValue("input-field", "monitor", Hyprlang::STRING{""}); m_config.addSpecialConfigValue("input-field", "size", Hyprlang::VEC2{400, 90}); @@ -151,6 +163,28 @@ std::vector CConfigManager::getWidgetConfigs() { // clang-format on } + // + keys = m_config.listKeysForSpecialCategory("image"); + for (auto& k : keys) { + // clang-format off + result.push_back(CConfigManager::SWidgetConfig{ + "image", + std::any_cast(m_config.getSpecialConfigValue("image", "monitor", k.c_str())), + { + {"path", m_config.getSpecialConfigValue("image", "path", k.c_str())}, + {"size", m_config.getSpecialConfigValue("image", "size", k.c_str())}, + {"rounding", m_config.getSpecialConfigValue("image", "rounding", k.c_str())}, + {"border_size", m_config.getSpecialConfigValue("image", "border_size", k.c_str())}, + {"border_color", m_config.getSpecialConfigValue("image", "border_color", k.c_str())}, + {"position", m_config.getSpecialConfigValue("image", "position", k.c_str())}, + {"halign", m_config.getSpecialConfigValue("image", "halign", k.c_str())}, + {"valign", m_config.getSpecialConfigValue("image", "valign", k.c_str())}, + SHADOWABLE("image"), + } + }); + // clang-format on + } + keys = m_config.listKeysForSpecialCategory("input-field"); for (auto& k : keys) { // clang-format off diff --git a/src/renderer/AsyncResourceGatherer.cpp b/src/renderer/AsyncResourceGatherer.cpp index b37331b..ae6e773 100644 --- a/src/renderer/AsyncResourceGatherer.cpp +++ b/src/renderer/AsyncResourceGatherer.cpp @@ -112,13 +112,13 @@ void CAsyncResourceGatherer::gather() { // gather resources to preload // clang-format off int preloads = std::count_if(CWIDGETS.begin(), CWIDGETS.end(), [](const auto& w) { - return w.type == "background"; + return w.type == "background" || w.type == "image"; }); // clang-format on progress = 0; for (auto& c : CWIDGETS) { - if (c.type == "background") { + if (c.type == "background" || c.type == "image") { #if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 180100 progress = progress + 1.0 / (preloads + 1.0); #else @@ -130,7 +130,7 @@ void CAsyncResourceGatherer::gather() { if (path.empty() || path == "screenshot") continue; - std::string id = std::string{"background:"} + path; + std::string id = (c.type == "background" ? std::string{"background:"} : std::string{"image:"}) + path; const auto ABSOLUTEPATH = absolutePath(path, ""); // preload bg img diff --git a/src/renderer/Renderer.cpp b/src/renderer/Renderer.cpp index eb1d6ec..436aa12 100644 --- a/src/renderer/Renderer.cpp +++ b/src/renderer/Renderer.cpp @@ -16,6 +16,7 @@ #include "widgets/PasswordInputField.hpp" #include "widgets/Background.hpp" #include "widgets/Label.hpp" +#include "widgets/Image.hpp" inline const float fullVerts[] = { 1, 0, // top right @@ -323,6 +324,14 @@ std::vector>* CRenderer::getOrCreateWidgetsFor(const CS widgets[surf].emplace_back(std::make_unique(surf->size, c.values)); } else if (c.type == "label") { widgets[surf].emplace_back(std::make_unique(surf->size, c.values, surf->output->stringPort)); + } else if (c.type == "image") { + const std::string PATH = std::any_cast(c.values.at("path")); + + std::string resourceID = ""; + if (!PATH.empty()) + resourceID = "image:" + PATH; + + widgets[surf].emplace_back(std::make_unique(surf->size, surf->output, resourceID, c.values)); } } } diff --git a/src/renderer/widgets/Image.cpp b/src/renderer/widgets/Image.cpp new file mode 100644 index 0000000..97c81ff --- /dev/null +++ b/src/renderer/widgets/Image.cpp @@ -0,0 +1,68 @@ +#include "Image.hpp" +#include "../Renderer.hpp" +#include + +CImage::CImage(const Vector2D& viewport_, COutput* output_, const std::string& resourceID_, const std::unordered_map& props) : + viewport(viewport_), resourceID(resourceID_), output(output_), shadow(this, props, viewport_) { + + size = std::any_cast(props.at("size")); + rounding = std::any_cast(props.at("rounding")); + border = std::any_cast(props.at("border_size")); + color = std::any_cast(props.at("border_color")); + pos = std::any_cast(props.at("position")); + halign = std::any_cast(props.at("halign")); + valign = std::any_cast(props.at("valign")); +} + +bool CImage::draw(const SRenderData& data) { + + if (resourceID.empty()) + return false; + + if (!asset) + asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID); + + if (!asset) + return true; + + if (asset->texture.m_iType == TEXTURE_INVALID) { + g_pRenderer->asyncResourceGatherer->unloadAsset(asset); + resourceID = ""; + return false; + } + + CTexture* tex = &asset->texture; + CBox texbox = {{}, tex->m_vSize}; + + if (firstRender) { + firstRender = false; + shadow.markShadowDirty(); + } + + const float SCALEX = size / tex->m_vSize.x; + const float SCALEY = size / tex->m_vSize.y; + + texbox.w *= std::max(SCALEX, SCALEY); + texbox.h *= std::max(SCALEX, SCALEY); + + shadow.draw(data); + + const bool ALLOWROUND = rounding > -1 && rounding < std::min(texbox.w, texbox.h) / 2.0; + const auto TEXPOS = posFromHVAlign(viewport, Vector2D{texbox.w, texbox.h}, pos, halign, valign); + + texbox.x = TEXPOS.x; + texbox.y = TEXPOS.y; + + if (border > 0) { + CBox borderBox = {TEXPOS - Vector2D{(double)border, (double)border}, texbox.size() + Vector2D{(double)border * 2.0, (double)border * 2.0}}; + CColor borderCol = color; + borderCol.a *= data.opacity; + borderBox.round(); + g_pRenderer->renderRect(borderBox, borderCol, ALLOWROUND ? rounding : std::min(borderBox.w, borderBox.h) / 2.0); + } + + texbox.round(); + g_pRenderer->renderTexture(texbox, *tex, data.opacity, ALLOWROUND ? rounding : std::min(texbox.w, texbox.h) / 2.0, WL_OUTPUT_TRANSFORM_FLIPPED_180); + + return data.opacity < 1.0; +} diff --git a/src/renderer/widgets/Image.hpp b/src/renderer/widgets/Image.hpp new file mode 100644 index 0000000..d473a1e --- /dev/null +++ b/src/renderer/widgets/Image.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "IWidget.hpp" +#include "../../helpers/Vector2D.hpp" +#include "../../helpers/Color.hpp" +#include "Shadowable.hpp" +#include +#include +#include + +struct SPreloadedAsset; +class COutput; + +class CImage : public IWidget { + public: + CImage(const Vector2D& viewport, COutput* output_, const std::string& resourceID, const std::unordered_map& props); + + virtual bool draw(const SRenderData& data); + + private: + int size; + int rounding; + int border; + CColor color; + Vector2D pos; + + std::string halign, valign; + + bool firstRender = true; + + Vector2D viewport; + std::string resourceID; + SPreloadedAsset* asset = nullptr; + COutput* output = nullptr; + CShadowable shadow; +};