mirror of
https://github.com/Trensa-Organization/hy3.git
synced 2025-03-16 03:03:40 +01:00
Usually the window workspace differs from the monitor workspace when moving a window to a background workspace. Previously onWindowCreatedTiling acted on the monitor active workspace which caused windows to always appear at the end of the outermost node on the target workspace. Now they appear relative to the last selected node.
684 lines
20 KiB
C++
684 lines
20 KiB
C++
#include "TabGroup.hpp"
|
|
|
|
#include <cairo/cairo.h>
|
|
#include <hyprland/src/Compositor.hpp>
|
|
#include <hyprland/src/helpers/Box.hpp>
|
|
#include <hyprland/src/helpers/Color.hpp>
|
|
#include <hyprland/src/render/OpenGL.hpp>
|
|
#include <pango/pangocairo.h>
|
|
#include <pixman.h>
|
|
|
|
#include "globals.hpp"
|
|
|
|
Hy3TabBarEntry::Hy3TabBarEntry(Hy3TabBar& tab_bar, Hy3Node& node): tab_bar(tab_bar), node(node) {
|
|
this->focused.create(
|
|
AVARTYPE_FLOAT,
|
|
0.0f,
|
|
g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"),
|
|
nullptr,
|
|
AVARDAMAGE_NONE
|
|
);
|
|
|
|
this->urgent.create(
|
|
AVARTYPE_FLOAT,
|
|
0.0f,
|
|
g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"),
|
|
nullptr,
|
|
AVARDAMAGE_NONE
|
|
);
|
|
|
|
this->offset.create(
|
|
AVARTYPE_FLOAT,
|
|
-1.0f,
|
|
g_pConfigManager->getAnimationPropertyConfig("windowsMove"),
|
|
nullptr,
|
|
AVARDAMAGE_NONE
|
|
);
|
|
|
|
this->width.create(
|
|
AVARTYPE_FLOAT,
|
|
-1.0f,
|
|
g_pConfigManager->getAnimationPropertyConfig("windowsMove"),
|
|
nullptr,
|
|
AVARDAMAGE_NONE
|
|
);
|
|
|
|
this->vertical_pos.create(
|
|
AVARTYPE_FLOAT,
|
|
1.0f,
|
|
g_pConfigManager->getAnimationPropertyConfig("windowsIn"),
|
|
nullptr,
|
|
AVARDAMAGE_NONE
|
|
);
|
|
|
|
this->fade_opacity.create(
|
|
AVARTYPE_FLOAT,
|
|
0.0f,
|
|
g_pConfigManager->getAnimationPropertyConfig("windowsIn"),
|
|
nullptr,
|
|
AVARDAMAGE_NONE
|
|
);
|
|
|
|
this->focused.registerVar();
|
|
this->urgent.registerVar();
|
|
this->offset.registerVar();
|
|
this->width.registerVar();
|
|
this->vertical_pos.registerVar();
|
|
this->fade_opacity.registerVar();
|
|
|
|
auto update_callback = [this](void*) { this->tab_bar.dirty = true; };
|
|
|
|
this->focused.setUpdateCallback(update_callback);
|
|
this->urgent.setUpdateCallback(update_callback);
|
|
this->offset.setUpdateCallback(update_callback);
|
|
this->width.setUpdateCallback(update_callback);
|
|
this->vertical_pos.setUpdateCallback(update_callback);
|
|
this->fade_opacity.setUpdateCallback(update_callback);
|
|
|
|
this->window_title = node.getTitle();
|
|
this->urgent = node.isUrgent();
|
|
|
|
this->vertical_pos = 0.0;
|
|
this->fade_opacity = 1.0;
|
|
}
|
|
|
|
bool Hy3TabBarEntry::operator==(const Hy3Node& node) const { return this->node == node; }
|
|
|
|
bool Hy3TabBarEntry::operator==(const Hy3TabBarEntry& entry) const {
|
|
return this->node == entry.node;
|
|
}
|
|
|
|
void Hy3TabBarEntry::setFocused(bool focused) {
|
|
if (this->focused.goalf() != focused) {
|
|
this->focused = focused;
|
|
}
|
|
}
|
|
|
|
void Hy3TabBarEntry::setUrgent(bool urgent) {
|
|
if (urgent && this->focused.goalf() == 1.0) urgent = false;
|
|
if (this->urgent.goalf() != urgent) {
|
|
this->urgent = urgent;
|
|
}
|
|
}
|
|
|
|
void Hy3TabBarEntry::setWindowTitle(std::string title) {
|
|
if (this->window_title != title) {
|
|
this->window_title = title;
|
|
this->tab_bar.dirty = true;
|
|
}
|
|
}
|
|
|
|
void Hy3TabBarEntry::beginDestroy() {
|
|
this->destroying = true;
|
|
this->vertical_pos = 1.0;
|
|
this->fade_opacity = 0.0;
|
|
}
|
|
|
|
void Hy3TabBarEntry::unDestroy() {
|
|
this->destroying = false;
|
|
this->vertical_pos = 0.0;
|
|
this->fade_opacity = 1.0;
|
|
}
|
|
|
|
bool Hy3TabBarEntry::shouldRemove() {
|
|
return this->destroying && (this->vertical_pos.fl() == 1.0 || this->width.fl() == 0.0);
|
|
}
|
|
|
|
void Hy3TabBarEntry::prepareTexture(float scale, CBox& box) {
|
|
// clang-format off
|
|
static const auto* s_rounding = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:rounding")->intValue;
|
|
static const auto* render_text = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:render_text")->intValue;
|
|
static const auto* text_center = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:text_center")->intValue;
|
|
static const auto* text_font = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:text_font")->strValue;
|
|
static const auto* text_height = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:text_height")->intValue;
|
|
static const auto* text_padding = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:text_padding")->intValue;
|
|
static const auto* col_active = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.active")->intValue;
|
|
static const auto* col_urgent = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.urgent")->intValue;
|
|
static const auto* col_inactive = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.inactive")->intValue;
|
|
static const auto* col_text_active = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.text.active")->intValue;
|
|
static const auto* col_text_urgent = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.text.urgent")->intValue;
|
|
static const auto* col_text_inactive = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.text.inactive")->intValue;
|
|
// clang-format on
|
|
|
|
auto width = box.width;
|
|
auto height = box.height;
|
|
|
|
auto rounding = std::min((double) *s_rounding * scale, std::min(width * 0.5, height * 0.5));
|
|
|
|
if (this->texture.m_iTexID == 0
|
|
// clang-format off
|
|
|| this->last_render.x != box.x
|
|
|| this->last_render.y != box.y
|
|
|| this->last_render.focused != this->focused.fl()
|
|
|| this->last_render.urgent != this->urgent.fl()
|
|
|| this->last_render.window_title != this->window_title
|
|
|| this->last_render.rounding != rounding
|
|
|| this->last_render.text_font != *text_font
|
|
|| this->last_render.text_height != *text_height
|
|
|| this->last_render.text_padding != *text_padding
|
|
|| this->last_render.col_active != *col_active
|
|
|| this->last_render.col_urgent != *col_urgent
|
|
|| this->last_render.col_inactive != *col_inactive
|
|
|| this->last_render.col_text_active != *col_text_active
|
|
|| this->last_render.col_text_urgent != *col_text_urgent
|
|
|| this->last_render.col_text_inactive != *col_text_inactive
|
|
// clang-format on
|
|
)
|
|
{
|
|
this->last_render.x = box.x;
|
|
this->last_render.y = box.y;
|
|
this->last_render.focused = this->focused.fl();
|
|
this->last_render.urgent = this->urgent.fl();
|
|
this->last_render.window_title = this->window_title;
|
|
this->last_render.rounding = rounding;
|
|
this->last_render.text_font = *text_font;
|
|
this->last_render.text_height = *text_height;
|
|
this->last_render.text_padding = *text_padding;
|
|
this->last_render.col_active = *col_active;
|
|
this->last_render.col_urgent = *col_urgent;
|
|
this->last_render.col_inactive = *col_inactive;
|
|
this->last_render.col_text_active = *col_text_active;
|
|
this->last_render.col_text_urgent = *col_text_urgent;
|
|
this->last_render.col_text_inactive = *col_text_inactive;
|
|
|
|
auto cairo_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
|
|
auto cairo = cairo_create(cairo_surface);
|
|
|
|
// clear pixmap
|
|
cairo_save(cairo);
|
|
cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
|
|
cairo_paint(cairo);
|
|
cairo_restore(cairo);
|
|
|
|
// set brush
|
|
auto focused = this->focused.fl();
|
|
auto urgent = this->urgent.fl();
|
|
auto inactive = 1.0 - (focused + urgent);
|
|
auto c = (CColor(*col_active) * focused) + (CColor(*col_urgent) * urgent)
|
|
+ (CColor(*col_inactive) * inactive);
|
|
|
|
cairo_set_source_rgba(cairo, c.r, c.g, c.b, c.a);
|
|
|
|
// outline bar shape
|
|
cairo_move_to(cairo, 0, rounding);
|
|
cairo_arc(cairo, rounding, rounding, rounding, -180.0 * (M_PI / 180.0), -90.0 * (M_PI / 180.0));
|
|
cairo_line_to(cairo, width - rounding, 0);
|
|
cairo_arc(cairo, width - rounding, rounding, rounding, -90.0 * (M_PI / 180.0), 0.0);
|
|
cairo_line_to(cairo, width, height - rounding);
|
|
cairo_arc(cairo, width - rounding, height - rounding, rounding, 0.0, 90.0 * (M_PI / 180.0));
|
|
cairo_line_to(cairo, rounding, height);
|
|
cairo_arc(
|
|
cairo,
|
|
rounding,
|
|
height - rounding,
|
|
rounding,
|
|
-270.0 * (M_PI / 180.0),
|
|
-180.0 * (M_PI / 180.0)
|
|
);
|
|
cairo_close_path(cairo);
|
|
|
|
// draw
|
|
cairo_fill(cairo);
|
|
|
|
// render window title
|
|
if (*render_text) {
|
|
PangoLayout* layout = pango_cairo_create_layout(cairo);
|
|
pango_layout_set_text(layout, this->window_title.c_str(), -1);
|
|
|
|
if (*text_center) pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
|
|
|
|
PangoFontDescription* font_desc = pango_font_description_from_string(text_font->c_str());
|
|
pango_font_description_set_size(font_desc, *text_height * scale * PANGO_SCALE);
|
|
pango_layout_set_font_description(layout, font_desc);
|
|
pango_font_description_free(font_desc);
|
|
|
|
int padding = *text_padding * scale;
|
|
int width = box.width - padding * 2;
|
|
|
|
pango_layout_set_width(layout, width * PANGO_SCALE);
|
|
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
|
|
|
|
auto c = (CColor(*col_text_active) * focused) + (CColor(*col_text_urgent) * urgent)
|
|
+ (CColor(*col_text_inactive) * inactive);
|
|
|
|
cairo_set_source_rgba(cairo, c.r, c.g, c.b, c.a);
|
|
|
|
int layout_width, layout_height;
|
|
pango_layout_get_size(layout, &layout_width, &layout_height);
|
|
|
|
auto y_offset = (height / 2.0) - (((double) layout_height / PANGO_SCALE) / 2.0);
|
|
cairo_move_to(cairo, padding, y_offset);
|
|
pango_cairo_show_layout(cairo, layout);
|
|
g_object_unref(layout);
|
|
}
|
|
|
|
// flush cairo
|
|
cairo_surface_flush(cairo_surface);
|
|
|
|
auto data = cairo_image_surface_get_data(cairo_surface);
|
|
this->texture.allocate();
|
|
|
|
glBindTexture(GL_TEXTURE_2D, this->texture.m_iTexID);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
|
#ifdef GLES32
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
|
#endif
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
|
|
|
cairo_destroy(cairo);
|
|
cairo_surface_destroy(cairo_surface);
|
|
} else {
|
|
glBindTexture(GL_TEXTURE_2D, this->texture.m_iTexID);
|
|
}
|
|
}
|
|
|
|
Hy3TabBar::Hy3TabBar() {
|
|
this->fade_opacity.create(
|
|
AVARTYPE_FLOAT,
|
|
1.0f,
|
|
g_pConfigManager->getAnimationPropertyConfig("windowsMove"),
|
|
nullptr,
|
|
AVARDAMAGE_NONE
|
|
);
|
|
|
|
this->fade_opacity.registerVar();
|
|
this->fade_opacity.setUpdateCallback([this](void*) { this->dirty = true; });
|
|
}
|
|
|
|
void Hy3TabBar::beginDestroy() {
|
|
for (auto& entry: this->entries) {
|
|
entry.beginDestroy();
|
|
}
|
|
}
|
|
|
|
void Hy3TabBar::tick() {
|
|
auto iter = this->entries.begin();
|
|
|
|
while (iter != this->entries.end()) {
|
|
if (iter->shouldRemove()) iter = this->entries.erase(iter);
|
|
else iter = std::next(iter);
|
|
}
|
|
|
|
if (this->entries.empty()) this->destroy = true;
|
|
}
|
|
|
|
void Hy3TabBar::updateNodeList(std::list<Hy3Node*>& nodes) {
|
|
std::list<std::list<Hy3TabBarEntry>::iterator> removed_entries;
|
|
|
|
auto entry = this->entries.begin();
|
|
auto node = nodes.begin();
|
|
|
|
// move any out of order entries to removed_entries
|
|
while (node != nodes.end()) {
|
|
while (true) {
|
|
if (entry == this->entries.end()) goto exitloop;
|
|
if (*entry == **node) break;
|
|
removed_entries.push_back(entry);
|
|
entry = std::next(entry);
|
|
}
|
|
|
|
node = std::next(node);
|
|
entry = std::next(entry);
|
|
}
|
|
|
|
exitloop:
|
|
|
|
// move any extra entries to removed_entries
|
|
while (entry != this->entries.end()) {
|
|
removed_entries.push_back(entry);
|
|
entry = std::next(entry);
|
|
}
|
|
|
|
entry = this->entries.begin();
|
|
node = nodes.begin();
|
|
|
|
// add missing entries, taking first from removed_entries
|
|
while (node != nodes.end()) {
|
|
if (entry == this->entries.end() || *entry != **node) {
|
|
if (std::find(removed_entries.begin(), removed_entries.end(), entry) != removed_entries.end())
|
|
{
|
|
entry = std::next(entry);
|
|
continue;
|
|
}
|
|
|
|
auto moved =
|
|
std::find_if(removed_entries.begin(), removed_entries.end(), [&node](auto entry) {
|
|
return **node == *entry;
|
|
});
|
|
|
|
if (moved != removed_entries.end()) {
|
|
this->entries.splice(entry, this->entries, *moved);
|
|
entry = *moved;
|
|
removed_entries.erase(moved);
|
|
} else {
|
|
entry = this->entries.emplace(entry, *this, **node);
|
|
}
|
|
}
|
|
|
|
entry->unDestroy();
|
|
|
|
// set stats from node data
|
|
auto* parent = (*node)->parent;
|
|
auto& parent_group = parent->data.as_group;
|
|
|
|
entry->setFocused(
|
|
parent_group.focused_child == *node
|
|
|| (parent_group.group_focused && parent->isIndirectlyFocused())
|
|
);
|
|
|
|
entry->setUrgent((*node)->isUrgent());
|
|
entry->setWindowTitle((*node)->getTitle());
|
|
|
|
node = std::next(node);
|
|
if (entry != this->entries.end()) entry = std::next(entry);
|
|
}
|
|
|
|
// initiate remove animations for any removed entries
|
|
for (auto& entry: removed_entries) {
|
|
if (!entry->destroying) entry->beginDestroy();
|
|
if (entry->shouldRemove()) this->entries.erase(entry);
|
|
}
|
|
}
|
|
|
|
void Hy3TabBar::updateAnimations(bool warp) {
|
|
int active_entries = 0;
|
|
for (auto& entry: this->entries) {
|
|
if (!entry.destroying) active_entries++;
|
|
}
|
|
|
|
float entry_width = active_entries == 0 ? 0.0 : 1.0 / active_entries;
|
|
float offset = 0.0;
|
|
|
|
auto entry = this->entries.begin();
|
|
while (entry != this->entries.end()) {
|
|
if (warp) {
|
|
if (entry->width.goalf() == 0.0) {
|
|
this->entries.erase(entry++);
|
|
continue;
|
|
}
|
|
|
|
entry->offset.setValueAndWarp(offset);
|
|
entry->width.setValueAndWarp(entry_width);
|
|
} else {
|
|
auto warp_init = entry->offset.goalf() == -1.0;
|
|
|
|
if (warp_init) {
|
|
entry->offset.setValueAndWarp(offset);
|
|
entry->width.setValueAndWarp(entry->vertical_pos.fl() == 0.0 ? 0.0 : entry_width);
|
|
}
|
|
|
|
if (!entry->destroying) {
|
|
if (entry->offset.goalf() != offset) entry->offset = offset;
|
|
if ((warp_init || entry->width.goalf() != 0.0) && entry->width.goalf() != entry_width)
|
|
entry->width = entry_width;
|
|
}
|
|
}
|
|
|
|
if (!entry->destroying) offset += entry->width.goalf();
|
|
entry = std::next(entry);
|
|
}
|
|
}
|
|
|
|
void Hy3TabBar::setSize(Vector2D size) {
|
|
if (size == this->size) return;
|
|
this->size = size;
|
|
}
|
|
|
|
Hy3TabGroup::Hy3TabGroup(Hy3Node& node) {
|
|
this->pos.create(
|
|
AVARTYPE_VECTOR,
|
|
g_pConfigManager->getAnimationPropertyConfig("windowsMove"),
|
|
nullptr,
|
|
AVARDAMAGE_NONE
|
|
);
|
|
|
|
this->size.create(
|
|
AVARTYPE_VECTOR,
|
|
g_pConfigManager->getAnimationPropertyConfig("windowsMove"),
|
|
nullptr,
|
|
AVARDAMAGE_NONE
|
|
);
|
|
|
|
this->pos.registerVar();
|
|
this->size.registerVar();
|
|
|
|
this->updateWithGroup(node, true);
|
|
this->pos.warp();
|
|
this->size.warp();
|
|
}
|
|
|
|
void Hy3TabGroup::updateWithGroup(Hy3Node& node, bool warp) {
|
|
static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue;
|
|
static const auto* gaps_out = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_out")->intValue;
|
|
static const auto* bar_height =
|
|
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:height")->intValue;
|
|
|
|
auto gaps = node.parent == nullptr ? *gaps_out : *gaps_in;
|
|
auto tpos = node.position + Vector2D(gaps, gaps) + node.gap_topleft_offset;
|
|
auto tsize = Vector2D(
|
|
node.size.x - node.gap_bottomright_offset.x - node.gap_topleft_offset.x - gaps * 2,
|
|
*bar_height
|
|
);
|
|
|
|
this->hidden = node.hidden;
|
|
if (this->pos.goalv() != tpos) {
|
|
this->pos = tpos;
|
|
if (warp) this->pos.warp();
|
|
}
|
|
|
|
if (this->size.goalv() != tsize) {
|
|
this->size = tsize;
|
|
if (warp) this->size.warp();
|
|
}
|
|
|
|
this->bar.updateNodeList(node.data.as_group.children);
|
|
this->bar.updateAnimations(warp);
|
|
|
|
if (node.data.as_group.focused_child != nullptr) {
|
|
this->updateStencilWindows(*node.data.as_group.focused_child);
|
|
}
|
|
}
|
|
|
|
void Hy3TabGroup::tick() {
|
|
static const auto* enter_from_top =
|
|
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:from_top")->intValue;
|
|
static const auto* padding =
|
|
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:padding")->intValue;
|
|
auto* workspace = g_pCompositor->getWorkspaceByID(this->workspace_id);
|
|
|
|
this->bar.tick();
|
|
|
|
if (workspace != nullptr) {
|
|
if (workspace->m_bHasFullscreenWindow) {
|
|
if (this->bar.fade_opacity.goalf() != 0.0) this->bar.fade_opacity = 0.0;
|
|
} else {
|
|
if (this->bar.fade_opacity.goalf() != 1.0) this->bar.fade_opacity = 1.0;
|
|
}
|
|
}
|
|
|
|
auto pos = this->pos.vec();
|
|
auto size = this->size.vec();
|
|
|
|
if (this->last_pos != pos || this->last_size != size) {
|
|
CBox damage_box = {this->last_pos.x, this->last_pos.y, this->last_size.x, this->last_size.y};
|
|
g_pHyprRenderer->damageBox(&damage_box);
|
|
|
|
this->bar.damaged = true;
|
|
this->last_pos = pos;
|
|
this->last_size = size;
|
|
}
|
|
|
|
if (this->bar.destroy || this->bar.dirty) {
|
|
// damage any area that could be covered by bar in/out animations
|
|
size.y = size.y * 2 + *padding;
|
|
if (*enter_from_top) {
|
|
pos.y -= *padding;
|
|
}
|
|
|
|
CBox damage_box = {pos.x, pos.y, size.x, size.y};
|
|
g_pHyprRenderer->damageBox(&damage_box);
|
|
|
|
this->bar.damaged = true;
|
|
this->bar.dirty = false;
|
|
}
|
|
}
|
|
|
|
void Hy3TabGroup::renderTabBar() {
|
|
static const auto* window_rounding =
|
|
&HyprlandAPI::getConfigValue(PHANDLE, "decoration:rounding")->intValue;
|
|
static const auto* enter_from_top =
|
|
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:from_top")->intValue;
|
|
static const auto* padding =
|
|
&HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:padding")->intValue;
|
|
|
|
auto* monitor = g_pHyprOpenGL->m_RenderData.pMonitor;
|
|
auto* workspace = g_pCompositor->getWorkspaceByID(this->workspace_id);
|
|
auto scale = monitor->scale;
|
|
|
|
auto monitor_size = monitor->vecSize;
|
|
auto pos = this->pos.vec() - monitor->vecPosition;
|
|
auto size = this->size.vec();
|
|
|
|
if (workspace != nullptr) {
|
|
pos = pos + workspace->m_vRenderOffset.vec();
|
|
}
|
|
|
|
auto scaled_pos = Vector2D(std::round(pos.x * scale), std::round(pos.y * scale));
|
|
auto scaled_size = Vector2D(std::round(size.x * scale), std::round(size.y * scale));
|
|
wlr_box box = {scaled_pos.x, scaled_pos.y, scaled_size.x, scaled_size.y};
|
|
|
|
// monitor size is not scaled
|
|
if (pos.x > monitor_size.x || pos.y > monitor_size.y || scaled_pos.x + scaled_size.x < 0
|
|
|| scaled_pos.y + scaled_size.y < 0)
|
|
return;
|
|
|
|
if (!this->bar.damaged) {
|
|
pixman_region32 damage;
|
|
pixman_region32_init(&damage);
|
|
|
|
pixman_region32_intersect_rect(
|
|
&damage,
|
|
g_pHyprOpenGL->m_RenderData.damage.pixman(),
|
|
box.x,
|
|
box.y,
|
|
box.width,
|
|
box.height
|
|
);
|
|
|
|
this->bar.damaged = pixman_region32_not_empty(&damage);
|
|
pixman_region32_fini(&damage);
|
|
}
|
|
|
|
if (!this->bar.damaged || this->bar.destroy) return;
|
|
this->bar.damaged = false;
|
|
|
|
this->bar.setSize(scaled_size);
|
|
|
|
{
|
|
glEnable(GL_STENCIL_TEST);
|
|
glClearStencil(0);
|
|
glClear(GL_STENCIL_BUFFER_BIT);
|
|
glStencilMask(0xff);
|
|
glStencilFunc(GL_ALWAYS, 1, 0xff);
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
|
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
|
|
|
for (auto* window: this->stencil_windows) {
|
|
if (!g_pCompositor->windowExists(window)) continue;
|
|
|
|
auto wpos = window->m_vRealPosition.vec() - monitor->vecPosition;
|
|
auto wsize = window->m_vRealSize.vec();
|
|
|
|
CBox window_box = {wpos.x, wpos.y, wsize.x, wsize.y};
|
|
// scaleBox(&window_box, scale);
|
|
window_box.scale(scale);
|
|
|
|
if (window_box.width > 0 && window_box.height > 0)
|
|
g_pHyprOpenGL->renderRect(&window_box, CColor(0, 0, 0, 0), *window_rounding);
|
|
}
|
|
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
|
glStencilMask(0x00);
|
|
glStencilFunc(GL_EQUAL, 0, 0xff);
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
|
}
|
|
|
|
auto fade_opacity =
|
|
this->bar.fade_opacity.fl() * (workspace == nullptr ? 1.0 : workspace->m_fAlpha.fl());
|
|
|
|
auto render_entry = [&](Hy3TabBarEntry& entry) {
|
|
Vector2D entry_pos = {
|
|
(pos.x + (entry.offset.fl() * size.x) + (*padding * 0.5)) * scale,
|
|
scaled_pos.y
|
|
+ ((entry.vertical_pos.fl() * (size.y + *padding) * scale) * (*enter_from_top ? -1 : 1)
|
|
),
|
|
};
|
|
Vector2D entry_size = {((entry.width.fl() * size.x) - *padding) * scale, scaled_size.y};
|
|
if (entry_size.x < 0 || entry_size.y < 0 || fade_opacity == 0.0) return;
|
|
|
|
CBox box = {
|
|
entry_pos.x,
|
|
entry_pos.y,
|
|
entry_size.x,
|
|
entry_size.y,
|
|
};
|
|
|
|
entry.prepareTexture(scale, box);
|
|
g_pHyprOpenGL->renderTexture(entry.texture, &box, fade_opacity * entry.fade_opacity.fl());
|
|
};
|
|
|
|
for (auto& entry: this->bar.entries) {
|
|
if (entry.focused.goalf() == 1.0) continue;
|
|
render_entry(entry);
|
|
}
|
|
|
|
for (auto& entry: this->bar.entries) {
|
|
if (entry.focused.goalf() == 0.0) continue;
|
|
render_entry(entry);
|
|
}
|
|
|
|
{
|
|
glClearStencil(0);
|
|
glClear(GL_STENCIL_BUFFER_BIT);
|
|
glDisable(GL_STENCIL_TEST);
|
|
glStencilMask(0xff);
|
|
glStencilFunc(GL_ALWAYS, 1, 0xff);
|
|
}
|
|
}
|
|
|
|
void findOverlappingWindows(Hy3Node& node, float height, std::vector<CWindow*>& windows) {
|
|
switch (node.data.type) {
|
|
case Hy3NodeType::Window: windows.push_back(node.data.as_window); break;
|
|
case Hy3NodeType::Group:
|
|
auto& group = node.data.as_group;
|
|
|
|
switch (group.layout) {
|
|
case Hy3GroupLayout::SplitH:
|
|
for (auto* node: group.children) {
|
|
findOverlappingWindows(*node, height, windows);
|
|
}
|
|
break;
|
|
case Hy3GroupLayout::SplitV:
|
|
for (auto* node: group.children) {
|
|
findOverlappingWindows(*node, height, windows);
|
|
height -= node->size.y;
|
|
if (height <= 0) break;
|
|
}
|
|
break;
|
|
case Hy3GroupLayout::Tabbed:
|
|
// assume the height of that node's tab bar already pushes it out of range
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Hy3TabGroup::updateStencilWindows(Hy3Node& group) {
|
|
this->stencil_windows.clear();
|
|
findOverlappingWindows(group, this->size.goalv().y, this->stencil_windows);
|
|
}
|