mirror of
https://github.com/hyprwm/hyprlock.git
synced 2024-12-22 13:29:48 +01:00
widgets: add Shape + minor image improve (#266)
* widgets: add Shape * image: minor improvment * glClear on FB alloc * add Nix HM * always create FB if not xray
This commit is contained in:
parent
d9f6441254
commit
eb1123fa2e
7 changed files with 290 additions and 9 deletions
|
@ -179,6 +179,95 @@ in {
|
|||
];
|
||||
};
|
||||
|
||||
shapes = mkOption {
|
||||
description = "Shape configurations";
|
||||
type = listOf (submodule {
|
||||
options = {
|
||||
monitor = mkOption {
|
||||
description = "The monitor to draw a shape";
|
||||
type = str;
|
||||
default = "";
|
||||
};
|
||||
|
||||
size = {
|
||||
x = mkOption {
|
||||
description = "Width of the shape";
|
||||
type = int;
|
||||
default = 360;
|
||||
};
|
||||
y = mkOption {
|
||||
description = "Height of the shape";
|
||||
type = int;
|
||||
default = 60;
|
||||
};
|
||||
};
|
||||
|
||||
color = mkOption {
|
||||
description = "Color of the shape";
|
||||
type = str;
|
||||
default = "rgba(22, 17, 17, 1.0)";
|
||||
};
|
||||
|
||||
rounding = mkOption {
|
||||
description = "Rounding of the shape";
|
||||
type = int;
|
||||
default = -1;
|
||||
};
|
||||
|
||||
border_size = mkOption {
|
||||
description = "Size of shape border";
|
||||
type = int;
|
||||
default = 4;
|
||||
};
|
||||
|
||||
border_color = mkOption {
|
||||
description = "Color of shape border";
|
||||
type = str;
|
||||
default = "rgba(0, 207, 230, 1.0)";
|
||||
};
|
||||
|
||||
rotate = mkOption {
|
||||
description = "Shape rotation angle";
|
||||
type = float;
|
||||
default = 0.0;
|
||||
};
|
||||
|
||||
xray = mkOption {
|
||||
description = "Whether to make a transparent \"hole\" in the background";
|
||||
type = bool;
|
||||
default = false;
|
||||
};
|
||||
|
||||
position = {
|
||||
x = mkOption {
|
||||
description = "X position of the shape";
|
||||
type = int;
|
||||
default = 0;
|
||||
};
|
||||
y = mkOption {
|
||||
description = "Y position of the shape";
|
||||
type = int;
|
||||
default = 80;
|
||||
};
|
||||
};
|
||||
|
||||
halign = mkOption {
|
||||
description = "Horizontal alignment of the shape";
|
||||
type = str;
|
||||
default = "center";
|
||||
};
|
||||
|
||||
valign = mkOption {
|
||||
description = "Vertical alignment of the shape";
|
||||
type = str;
|
||||
default = "center";
|
||||
};
|
||||
}
|
||||
// shadow;
|
||||
});
|
||||
default = [];
|
||||
};
|
||||
|
||||
images = mkOption {
|
||||
description = "Image configurations";
|
||||
type = listOf (submodule {
|
||||
|
@ -561,6 +650,24 @@ in {
|
|||
'')
|
||||
cfg.backgrounds)}
|
||||
|
||||
${builtins.concatStringsSep "\n" (map (shape: ''
|
||||
shape {
|
||||
monitor = ${shape.monitor}
|
||||
size = ${toString shape.size.x}, ${toString shape.size.y}
|
||||
color = ${shape.color}
|
||||
rounding = ${toString shape.rounding}
|
||||
border_size = ${toString shape.border_size}
|
||||
border_color = ${shape.border_color}
|
||||
rotate = ${toString shape.rotate}
|
||||
xray = ${boolToString shape.xray}
|
||||
|
||||
position = ${toString shape.position.x}, ${toString shape.position.y}
|
||||
halign = ${shape.halign}
|
||||
valign = ${shape.valign}
|
||||
}
|
||||
'')
|
||||
cfg.shapes)}
|
||||
|
||||
${builtins.concatStringsSep "\n" (map (image: ''
|
||||
image {
|
||||
monitor = ${image.monitor}
|
||||
|
|
|
@ -62,6 +62,20 @@ 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("shape", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
||||
m_config.addSpecialConfigValue("shape", "monitor", Hyprlang::STRING{""});
|
||||
m_config.addSpecialConfigValue("shape", "size", Hyprlang::VEC2{100, 100});
|
||||
m_config.addSpecialConfigValue("shape", "rounding", Hyprlang::INT{0});
|
||||
m_config.addSpecialConfigValue("shape", "border_size", Hyprlang::INT{0});
|
||||
m_config.addSpecialConfigValue("shape", "border_color", Hyprlang::INT{0xFF00CFE6});
|
||||
m_config.addSpecialConfigValue("shape", "color", Hyprlang::INT{0xFF111111});
|
||||
m_config.addSpecialConfigValue("shape", "position", Hyprlang::VEC2{0, 80});
|
||||
m_config.addSpecialConfigValue("shape", "halign", Hyprlang::STRING{"center"});
|
||||
m_config.addSpecialConfigValue("shape", "valign", Hyprlang::STRING{"center"});
|
||||
m_config.addSpecialConfigValue("shape", "rotate", Hyprlang::FLOAT{0});
|
||||
m_config.addSpecialConfigValue("shape", "xray", Hyprlang::INT{0});
|
||||
SHADOWABLE("shape");
|
||||
|
||||
m_config.addSpecialCategory("image", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
||||
m_config.addSpecialConfigValue("image", "monitor", Hyprlang::STRING{""});
|
||||
m_config.addSpecialConfigValue("image", "path", Hyprlang::STRING{""});
|
||||
|
@ -169,6 +183,30 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
|
|||
// clang-format on
|
||||
}
|
||||
|
||||
//
|
||||
keys = m_config.listKeysForSpecialCategory("shape");
|
||||
for (auto& k : keys) {
|
||||
// clang-format off
|
||||
result.push_back(CConfigManager::SWidgetConfig{
|
||||
"shape",
|
||||
std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("shape", "monitor", k.c_str())),
|
||||
{
|
||||
{"size", m_config.getSpecialConfigValue("shape", "size", k.c_str())},
|
||||
{"rounding", m_config.getSpecialConfigValue("shape", "rounding", k.c_str())},
|
||||
{"border_size", m_config.getSpecialConfigValue("shape", "border_size", k.c_str())},
|
||||
{"border_color", m_config.getSpecialConfigValue("shape", "border_color", k.c_str())},
|
||||
{"color", m_config.getSpecialConfigValue("shape", "color", k.c_str())},
|
||||
{"position", m_config.getSpecialConfigValue("shape", "position", k.c_str())},
|
||||
{"halign", m_config.getSpecialConfigValue("shape", "halign", k.c_str())},
|
||||
{"valign", m_config.getSpecialConfigValue("shape", "valign", k.c_str())},
|
||||
{"rotate", m_config.getSpecialConfigValue("shape", "rotate", k.c_str())},
|
||||
{"xray", m_config.getSpecialConfigValue("shape", "xray", k.c_str())},
|
||||
SHADOWABLE("shape"),
|
||||
}
|
||||
});
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
//
|
||||
keys = m_config.listKeysForSpecialCategory("image");
|
||||
for (auto& k : keys) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "widgets/Background.hpp"
|
||||
#include "widgets/Label.hpp"
|
||||
#include "widgets/Image.hpp"
|
||||
#include "widgets/Shape.hpp"
|
||||
|
||||
inline const float fullVerts[] = {
|
||||
1, 0, // top right
|
||||
|
@ -324,6 +325,8 @@ std::vector<std::unique_ptr<IWidget>>* CRenderer::getOrCreateWidgetsFor(const CS
|
|||
widgets[surf].emplace_back(std::make_unique<CPasswordInputField>(surf->size, c.values, surf->output->stringPort));
|
||||
} else if (c.type == "label") {
|
||||
widgets[surf].emplace_back(std::make_unique<CLabel>(surf->size, c.values, surf->output->stringPort));
|
||||
} else if (c.type == "shape") {
|
||||
widgets[surf].emplace_back(std::make_unique<CShape>(surf->size, c.values));
|
||||
} else if (c.type == "image") {
|
||||
const std::string PATH = std::any_cast<Hyprlang::STRING>(c.values.at("path"));
|
||||
|
||||
|
|
|
@ -132,12 +132,14 @@ bool CImage::draw(const SRenderData& data) {
|
|||
|
||||
if (!imageFB.isAllocated()) {
|
||||
|
||||
const Vector2D IMAGEPOS = {border, border};
|
||||
const Vector2D BORDERPOS = {0.0, 0.0};
|
||||
const Vector2D TEXSIZE = asset->texture.m_vSize;
|
||||
const float SCALEX = size / TEXSIZE.x;
|
||||
const float SCALEY = size / TEXSIZE.y;
|
||||
|
||||
// image with borders offset
|
||||
CBox texbox = {{border, border}, TEXSIZE};
|
||||
// image with borders offset, with extra pixel for anti-aliasing when rotated
|
||||
CBox texbox = {angle == 0 ? IMAGEPOS : IMAGEPOS + Vector2D{1.0, 1.0}, TEXSIZE};
|
||||
|
||||
texbox.w *= std::max(SCALEX, SCALEY);
|
||||
texbox.h *= std::max(SCALEX, SCALEY);
|
||||
|
@ -145,16 +147,19 @@ bool CImage::draw(const SRenderData& data) {
|
|||
const bool ALLOWROUND = rounding > -1 && rounding < std::min(texbox.w, texbox.h) / 2.0;
|
||||
|
||||
// plus borders if any
|
||||
CBox borderBox = {{}, {texbox.w + border * 2.0, texbox.h + border * 2.0}};
|
||||
CBox borderBox = {angle == 0 ? BORDERPOS : BORDERPOS + Vector2D{1.0, 1.0}, texbox.size() + IMAGEPOS * 2.0};
|
||||
|
||||
borderBox.round();
|
||||
imageFB.alloc(borderBox.w, borderBox.h, true);
|
||||
|
||||
const Vector2D FBSIZE = angle == 0 ? borderBox.size() : borderBox.size() + Vector2D{2.0, 2.0};
|
||||
|
||||
imageFB.alloc(FBSIZE.x, FBSIZE.y, true);
|
||||
g_pRenderer->pushFb(imageFB.m_iFb);
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (border > 0)
|
||||
g_pRenderer->renderRect(borderBox, color, ALLOWROUND ? rounding : std::min(borderBox.w, borderBox.h) / 2.0);
|
||||
g_pRenderer->renderRect(borderBox, color, ALLOWROUND ? (rounding == 0 ? 0 : rounding + std::round(border / M_PI)) : std::min(borderBox.w, borderBox.h) / 2.0);
|
||||
|
||||
texbox.round();
|
||||
g_pRenderer->renderTexture(texbox, asset->texture, 1.0, ALLOWROUND ? rounding : std::min(texbox.w, texbox.h) / 2.0, WL_OUTPUT_TRANSFORM_NORMAL);
|
||||
|
|
|
@ -116,12 +116,13 @@ bool CLabel::draw(const SRenderData& data) {
|
|||
}
|
||||
}
|
||||
|
||||
shadow.draw(data);
|
||||
|
||||
// calc pos
|
||||
pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign, angle);
|
||||
|
||||
CBox box = {pos.x, pos.y, asset->texture.m_vSize.x, asset->texture.m_vSize.y};
|
||||
box.rot = angle;
|
||||
shadow.draw(data);
|
||||
g_pRenderer->renderTexture(box, asset->texture, data.opacity);
|
||||
|
||||
return false;
|
||||
|
|
89
src/renderer/widgets/Shape.cpp
Normal file
89
src/renderer/widgets/Shape.cpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
#include "Shape.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
#include <cmath>
|
||||
|
||||
CShape::CShape(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props) : shadow(this, props, viewport_) {
|
||||
|
||||
size = std::any_cast<Hyprlang::VEC2>(props.at("size"));
|
||||
rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
|
||||
border = std::any_cast<Hyprlang::INT>(props.at("border_size"));
|
||||
color = std::any_cast<Hyprlang::INT>(props.at("color"));
|
||||
borderColor = std::any_cast<Hyprlang::INT>(props.at("border_color"));
|
||||
pos = std::any_cast<Hyprlang::VEC2>(props.at("position"));
|
||||
halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
|
||||
valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
|
||||
angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
|
||||
xray = std::any_cast<Hyprlang::INT>(props.at("xray"));
|
||||
|
||||
viewport = viewport_;
|
||||
angle = angle * M_PI / 180.0;
|
||||
|
||||
const Vector2D VBORDER = {border, border};
|
||||
const Vector2D REALSIZE = size + VBORDER * 2.0;
|
||||
const Vector2D OFFSET = angle == 0 ? Vector2D{0.0, 0.0} : Vector2D{1.0, 1.0};
|
||||
|
||||
pos = posFromHVAlign(viewport, xray ? size : REALSIZE + OFFSET * 2.0, pos, halign, valign, xray ? 0 : angle);
|
||||
|
||||
if (xray) {
|
||||
shapeBox = {pos, size};
|
||||
borderBox = {pos - VBORDER, REALSIZE};
|
||||
} else {
|
||||
shapeBox = {OFFSET + VBORDER, size};
|
||||
borderBox = {OFFSET, REALSIZE};
|
||||
}
|
||||
}
|
||||
|
||||
bool CShape::draw(const SRenderData& data) {
|
||||
|
||||
if (firstRender) {
|
||||
firstRender = false;
|
||||
shadow.markShadowDirty();
|
||||
}
|
||||
|
||||
shadow.draw(data);
|
||||
|
||||
const auto MINHALFBORDER = std::min(borderBox.w, borderBox.h) / 2.0;
|
||||
|
||||
if (xray) {
|
||||
if (border > 0) {
|
||||
const int PIROUND = std::min(MINHALFBORDER, std::round(border * M_PI));
|
||||
CColor borderCol = borderColor;
|
||||
borderCol.a *= data.opacity;
|
||||
g_pRenderer->renderRect(borderBox, borderCol, rounding == -1 ? PIROUND : std::clamp(rounding, 0, PIROUND));
|
||||
}
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(shapeBox.x, shapeBox.y, shapeBox.width, shapeBox.height);
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
return data.opacity < 1.0;
|
||||
}
|
||||
|
||||
if (!shapeFB.isAllocated()) {
|
||||
const auto MINHALFSHAPE = std::min(shapeBox.w, shapeBox.h) / 2.0;
|
||||
const bool ALLOWROUND = rounding > -1 && rounding < MINHALFSHAPE;
|
||||
|
||||
shapeFB.alloc(borderBox.width + borderBox.x * 2.0, borderBox.height + borderBox.y * 2.0, true);
|
||||
g_pRenderer->pushFb(shapeFB.m_iFb);
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (border > 0)
|
||||
g_pRenderer->renderRect(borderBox, borderColor, ALLOWROUND ? (rounding == 0 ? 0 : rounding + std::round(border / M_PI)) : MINHALFBORDER);
|
||||
|
||||
g_pRenderer->renderRect(shapeBox, color, ALLOWROUND ? rounding : MINHALFSHAPE);
|
||||
g_pRenderer->popFb();
|
||||
}
|
||||
|
||||
CTexture* tex = &shapeFB.m_cTex;
|
||||
CBox texbox = {pos, tex->m_vSize};
|
||||
|
||||
texbox.round();
|
||||
texbox.rot = angle;
|
||||
|
||||
g_pRenderer->renderTexture(texbox, *tex, data.opacity, 0, WL_OUTPUT_TRANSFORM_FLIPPED_180);
|
||||
|
||||
return data.opacity < 1.0;
|
||||
}
|
38
src/renderer/widgets/Shape.hpp
Normal file
38
src/renderer/widgets/Shape.hpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include "IWidget.hpp"
|
||||
#include "../../helpers/Vector2D.hpp"
|
||||
#include "../../helpers/Color.hpp"
|
||||
#include "../../helpers/Box.hpp"
|
||||
#include "Shadowable.hpp"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <any>
|
||||
|
||||
class CShape : public IWidget {
|
||||
public:
|
||||
CShape(const Vector2D& viewport, const std::unordered_map<std::string, std::any>& props);
|
||||
|
||||
virtual bool draw(const SRenderData& data);
|
||||
|
||||
private:
|
||||
CFramebuffer shapeFB;
|
||||
|
||||
int rounding;
|
||||
double border;
|
||||
double angle;
|
||||
CColor color;
|
||||
CColor borderColor;
|
||||
Vector2D size;
|
||||
Vector2D pos;
|
||||
CBox shapeBox;
|
||||
CBox borderBox;
|
||||
bool xray;
|
||||
|
||||
std::string halign, valign;
|
||||
|
||||
bool firstRender = true;
|
||||
|
||||
Vector2D viewport;
|
||||
CShadowable shadow;
|
||||
};
|
Loading…
Reference in a new issue