mirror of
https://github.com/hyprwm/hyprlock.git
synced 2024-12-22 13:29:48 +01:00
widgets: add rotate
option to label and image (#234)
* widgets: add `rotate` option to label and image * use framebuffer for image
This commit is contained in:
parent
7f8c9b6add
commit
84c6282d6a
11 changed files with 90 additions and 32 deletions
|
@ -219,6 +219,12 @@ in {
|
|||
default = "rgb(221, 221, 221)";
|
||||
};
|
||||
|
||||
rotate = mkOption {
|
||||
description = "Image rotation angle";
|
||||
type = float;
|
||||
default = 0;
|
||||
};
|
||||
|
||||
position = {
|
||||
x = mkOption {
|
||||
description = "X position of the image";
|
||||
|
@ -471,6 +477,12 @@ in {
|
|||
default = "Noto Sans";
|
||||
};
|
||||
|
||||
rotate = mkOption {
|
||||
description = "Label rotation angle";
|
||||
type = float;
|
||||
default = 0;
|
||||
};
|
||||
|
||||
position = {
|
||||
x = mkOption {
|
||||
description = "X position of the label";
|
||||
|
@ -544,6 +556,7 @@ in {
|
|||
rounding = ${toString image.rounding}
|
||||
border_size = ${toString image.border_size}
|
||||
border_color = ${image.border_color}
|
||||
rotate = ${toString image.rotate}
|
||||
|
||||
position = ${toString image.position.x}, ${toString image.position.y}
|
||||
halign = ${image.halign}
|
||||
|
@ -597,6 +610,7 @@ in {
|
|||
color = ${label.color}
|
||||
font_size = ${toString label.font_size}
|
||||
font_family = ${label.font_family}
|
||||
rotate = ${toString label.rotate}
|
||||
shadow_passes = ${toString label.shadow_passes}
|
||||
shadow_size = ${toString label.shadow_size}
|
||||
shadow_color = ${label.shadow_color}
|
||||
|
|
|
@ -72,6 +72,7 @@ void CConfigManager::init() {
|
|||
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"});
|
||||
m_config.addSpecialConfigValue("image", "rotate", Hyprlang::FLOAT{0});
|
||||
SHADOWABLE("image");
|
||||
|
||||
m_config.addSpecialCategory("input-field", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
||||
|
@ -113,6 +114,7 @@ void CConfigManager::init() {
|
|||
m_config.addSpecialConfigValue("label", "font_family", Hyprlang::STRING{"Sans"});
|
||||
m_config.addSpecialConfigValue("label", "halign", Hyprlang::STRING{"none"});
|
||||
m_config.addSpecialConfigValue("label", "valign", Hyprlang::STRING{"none"});
|
||||
m_config.addSpecialConfigValue("label", "rotate", Hyprlang::FLOAT{0});
|
||||
SHADOWABLE("label");
|
||||
|
||||
m_config.registerHandler(&::handleSource, "source", {false});
|
||||
|
@ -181,6 +183,7 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
|
|||
{"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())},
|
||||
{"rotate", m_config.getSpecialConfigValue("image", "rotate", k.c_str())},
|
||||
SHADOWABLE("image"),
|
||||
}
|
||||
});
|
||||
|
@ -241,6 +244,7 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
|
|||
{"text", m_config.getSpecialConfigValue("label", "text", k.c_str())},
|
||||
{"halign", m_config.getSpecialConfigValue("label", "halign", k.c_str())},
|
||||
{"valign", m_config.getSpecialConfigValue("label", "valign", k.c_str())},
|
||||
{"rotate", m_config.getSpecialConfigValue("label", "rotate", k.c_str())},
|
||||
SHADOWABLE("label"),
|
||||
}
|
||||
});
|
||||
|
|
|
@ -49,3 +49,9 @@ double Vector2D::size() const {
|
|||
Vector2D Vector2D::getComponentMax(const Vector2D& other) const {
|
||||
return Vector2D(std::max(this->x, other.x), std::max(this->y, other.y));
|
||||
}
|
||||
|
||||
Vector2D Vector2D::rotated(const double& ang) const {
|
||||
const double COS = std::abs(std::cos(ang));
|
||||
const double SIN = std::abs(std::sin(ang));
|
||||
return Vector2D(x * COS + y * SIN, x * SIN + y * COS);
|
||||
}
|
||||
|
|
|
@ -98,6 +98,7 @@ class Vector2D {
|
|||
Vector2D round() const;
|
||||
|
||||
Vector2D getComponentMax(const Vector2D& other) const;
|
||||
Vector2D rotated(const double& ang) const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -226,7 +226,7 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf
|
|||
|
||||
void CRenderer::renderRect(const CBox& box, const CColor& col, int rounding) {
|
||||
float matrix[9];
|
||||
wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
|
||||
wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, box.rot,
|
||||
projMatrix.data()); // TODO: write own, don't use WLR here
|
||||
|
||||
float glMatrix[9];
|
||||
|
@ -258,7 +258,7 @@ void CRenderer::renderRect(const CBox& box, const CColor& col, int rounding) {
|
|||
|
||||
void CRenderer::renderTexture(const CBox& box, const CTexture& tex, float a, int rounding, std::optional<wl_output_transform> tr) {
|
||||
float matrix[9];
|
||||
wlr_matrix_project_box(matrix, &box, tr.value_or(WL_OUTPUT_TRANSFORM_FLIPPED_180) /* ugh coordinate spaces */, 0,
|
||||
wlr_matrix_project_box(matrix, &box, tr.value_or(WL_OUTPUT_TRANSFORM_FLIPPED_180) /* ugh coordinate spaces */, box.rot,
|
||||
projMatrix.data()); // TODO: write own, don't use WLR here
|
||||
|
||||
float glMatrix[9];
|
||||
|
|
|
@ -16,25 +16,31 @@ namespace std {
|
|||
}
|
||||
#endif
|
||||
|
||||
Vector2D IWidget::posFromHVAlign(const Vector2D& viewport, const Vector2D& size, const Vector2D& offset, const std::string& halign, const std::string& valign) {
|
||||
Vector2D IWidget::posFromHVAlign(const Vector2D& viewport, const Vector2D& size, const Vector2D& offset, const std::string& halign, const std::string& valign, const double& ang) {
|
||||
|
||||
// offset after rotation for alignment
|
||||
Vector2D rot;
|
||||
if (ang != 0)
|
||||
rot = (size - size.rotated(ang)) / 2.0;
|
||||
|
||||
Vector2D pos = offset;
|
||||
if (halign == "center")
|
||||
pos.x += viewport.x / 2.0 - size.x / 2.0;
|
||||
else if (halign == "left")
|
||||
pos.x += 0;
|
||||
pos.x += 0 - rot.x;
|
||||
else if (halign == "right")
|
||||
pos.x += viewport.x - size.x;
|
||||
pos.x += viewport.x - size.x + rot.x;
|
||||
else if (halign != "none")
|
||||
Debug::log(ERR, "IWidget: invalid halign {}", halign);
|
||||
|
||||
if (valign == "center")
|
||||
pos.y += viewport.y / 2.0 - size.y / 2.0;
|
||||
else if (valign == "top")
|
||||
pos.y += viewport.y - size.y;
|
||||
pos.y += viewport.y - size.y + rot.y;
|
||||
else if (valign == "bottom")
|
||||
pos.y += size.y;
|
||||
pos.y += 0 - rot.y;
|
||||
else if (valign != "none")
|
||||
Debug::log(ERR, "IWidget: invalid halign {}", halign);
|
||||
Debug::log(ERR, "IWidget: invalid valign {}", valign);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ class IWidget {
|
|||
|
||||
virtual bool draw(const SRenderData& data) = 0;
|
||||
|
||||
virtual Vector2D posFromHVAlign(const Vector2D& viewport, const Vector2D& size, const Vector2D& offset, const std::string& halign, const std::string& valign);
|
||||
virtual Vector2D posFromHVAlign(const Vector2D& viewport, const Vector2D& size, const Vector2D& offset, const std::string& halign, const std::string& valign,
|
||||
const double& ang = 0);
|
||||
|
||||
struct SFormatResult {
|
||||
std::string formatted;
|
||||
|
|
|
@ -12,6 +12,9 @@ CImage::CImage(const Vector2D& viewport_, COutput* output_, const std::string& r
|
|||
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"));
|
||||
|
||||
angle = angle * M_PI / 180.0;
|
||||
}
|
||||
|
||||
bool CImage::draw(const SRenderData& data) {
|
||||
|
@ -31,7 +34,36 @@ bool CImage::draw(const SRenderData& data) {
|
|||
return false;
|
||||
}
|
||||
|
||||
CTexture* tex = &asset->texture;
|
||||
if (!imageFB.isAllocated()) {
|
||||
|
||||
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};
|
||||
|
||||
texbox.w *= std::max(SCALEX, SCALEY);
|
||||
texbox.h *= std::max(SCALEX, SCALEY);
|
||||
|
||||
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}};
|
||||
|
||||
borderBox.round();
|
||||
imageFB.alloc(borderBox.w, borderBox.h, true);
|
||||
g_pRenderer->pushFb(imageFB.m_iFb);
|
||||
|
||||
if (border > 0)
|
||||
g_pRenderer->renderRect(borderBox, color, ALLOWROUND ? rounding : 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);
|
||||
g_pRenderer->popFb();
|
||||
}
|
||||
|
||||
CTexture* tex = &imageFB.m_cTex;
|
||||
CBox texbox = {{}, tex->m_vSize};
|
||||
|
||||
if (firstRender) {
|
||||
|
@ -39,30 +71,16 @@ bool CImage::draw(const SRenderData& data) {
|
|||
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);
|
||||
const auto TEXPOS = posFromHVAlign(viewport, tex->m_vSize, pos, halign, valign, angle);
|
||||
|
||||
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);
|
||||
texbox.rot = angle;
|
||||
g_pRenderer->renderTexture(texbox, *tex, data.opacity, 0, WL_OUTPUT_TRANSFORM_FLIPPED_180);
|
||||
|
||||
return data.opacity < 1.0;
|
||||
}
|
||||
|
|
|
@ -18,9 +18,12 @@ class CImage : public IWidget {
|
|||
virtual bool draw(const SRenderData& data);
|
||||
|
||||
private:
|
||||
CFramebuffer imageFB;
|
||||
|
||||
int size;
|
||||
int rounding;
|
||||
int border;
|
||||
double border;
|
||||
double angle;
|
||||
CColor color;
|
||||
Vector2D pos;
|
||||
|
||||
|
|
|
@ -87,6 +87,9 @@ CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string,
|
|||
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"));
|
||||
angle = angle * M_PI / 180.0;
|
||||
|
||||
plantTimer();
|
||||
}
|
||||
|
||||
|
@ -97,8 +100,6 @@ bool CLabel::draw(const SRenderData& data) {
|
|||
if (!asset)
|
||||
return true;
|
||||
|
||||
// calc pos
|
||||
pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign);
|
||||
shadow.markShadowDirty();
|
||||
}
|
||||
|
||||
|
@ -111,12 +112,15 @@ bool CLabel::draw(const SRenderData& data) {
|
|||
asset = newAsset;
|
||||
resourceID = pendingResourceID;
|
||||
pendingResourceID = "";
|
||||
pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign);
|
||||
shadow.markShadowDirty();
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ class CLabel : public IWidget {
|
|||
Vector2D viewport;
|
||||
Vector2D pos;
|
||||
Vector2D configPos;
|
||||
double angle;
|
||||
std::string resourceID;
|
||||
std::string pendingResourceID; // if dynamic label
|
||||
std::string halign, valign;
|
||||
|
@ -44,4 +45,4 @@ class CLabel : public IWidget {
|
|||
std::shared_ptr<CTimer> labelTimer;
|
||||
|
||||
CShadowable shadow;
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue