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:
bvr-yr 2024-03-29 22:01:11 +03:00 committed by GitHub
parent 7f8c9b6add
commit 84c6282d6a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 90 additions and 32 deletions

View File

@ -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}

View File

@ -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"),
}
});

View File

@ -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);
}

View File

@ -98,6 +98,7 @@ class Vector2D {
Vector2D round() const;
Vector2D getComponentMax(const Vector2D& other) const;
Vector2D rotated(const double& ang) const;
};
/**

View File

@ -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];

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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;
};
};