taskbar: re-use capsule radius for workspace's groups + improved "visibleWhen" property to support OR conditions

This commit is contained in:
Lemmy
2026-05-10 22:53:11 -04:00
parent 676cef81a3
commit c13281e2bb
10 changed files with 72 additions and 37 deletions
+2 -1
View File
@@ -981,7 +981,8 @@
},
"capsule_radius": {
"label": "Capsule Radius",
"description": "Corner radius for this widget capsule; leave blank for automatic pill shape"
"description": "Corner radius for this widget capsule; leave blank for automatic pill shape",
"taskbar-description": "Corner radius for this widget capsule and taskbar workspace groups; leave blank for automatic pill shape"
},
"color": {
"label": "Color",
+20 -19
View File
@@ -123,6 +123,26 @@ WidgetBarCapsuleSpec resolveWidgetBarCapsuleSpec(const BarConfig& bar, const Wid
} else {
spec.enabled = bar.widgetCapsuleDefault;
}
spec.padding = bar.widgetCapsulePadding;
if (widget != nullptr && widget->hasSetting("capsule_padding")) {
spec.padding = std::clamp(
static_cast<float>(widget->getDouble("capsule_padding", static_cast<double>(spec.padding))), 0.0f, 48.0f);
}
if (bar.widgetCapsuleRadius.has_value()) {
spec.radius = std::clamp(static_cast<float>(*bar.widgetCapsuleRadius), 0.0f, 80.0f);
}
if (widget != nullptr && widget->hasSetting("capsule_radius")) {
spec.radius = std::clamp(
static_cast<float>(widget->getDouble("capsule_radius", static_cast<double>(spec.radius.value_or(0.0f)))), 0.0f,
80.0f);
}
spec.opacity = bar.widgetCapsuleOpacity;
if (widget != nullptr && widget->hasSetting("capsule_opacity")) {
spec.opacity = std::clamp(
static_cast<float>(widget->getDouble("capsule_opacity", static_cast<double>(spec.opacity))), 0.0f, 1.0f);
}
if (!spec.enabled) {
return spec;
}
@@ -145,25 +165,6 @@ WidgetBarCapsuleSpec resolveWidgetBarCapsuleSpec(const BarConfig& bar, const Wid
spec.border = std::nullopt;
}
spec.padding = bar.widgetCapsulePadding;
if (widget != nullptr && widget->hasSetting("capsule_padding")) {
spec.padding = std::clamp(
static_cast<float>(widget->getDouble("capsule_padding", static_cast<double>(spec.padding))), 0.0f, 48.0f);
}
if (bar.widgetCapsuleRadius.has_value()) {
spec.radius = std::clamp(static_cast<float>(*bar.widgetCapsuleRadius), 0.0f, 80.0f);
}
if (widget != nullptr && widget->hasSetting("capsule_radius")) {
spec.radius = std::clamp(
static_cast<float>(widget->getDouble("capsule_radius", static_cast<double>(spec.radius.value_or(0.0f)))), 0.0f,
80.0f);
}
spec.opacity = bar.widgetCapsuleOpacity;
if (widget != nullptr && widget->hasSetting("capsule_opacity")) {
spec.opacity = std::clamp(
static_cast<float>(widget->getDouble("capsule_opacity", static_cast<double>(spec.opacity))), 0.0f, 1.0f);
}
if (widget != nullptr && widget->hasSetting("capsule_foreground")) {
spec.foreground = parseColorSpecString(widget->getString("capsule_foreground", ""));
} else if (bar.widgetCapsuleForeground.has_value()) {
+2 -1
View File
@@ -168,7 +168,8 @@ struct WidgetConfig {
bool operator==(const WidgetConfig&) const = default;
};
// Merges `[bar.*]` capsule defaults with `[widget.*]` overrides (see CONFIG.md).
// Merges `[bar.*]` capsule defaults with `[widget.*]` overrides (see CONFIG.md). Size/style fields such as
// `radius` are populated even when `enabled` is false so widgets can reuse capsule styling internally.
[[nodiscard]] WidgetBarCapsuleSpec resolveWidgetBarCapsuleSpec(const BarConfig& bar, const WidgetConfig* widget);
// Color spec for `[widget.*] color` and other user color strings (same rules as `capsule_fill`).
+3 -9
View File
@@ -38,14 +38,6 @@ namespace {
constexpr float kAutoHideSlideExtraPx = 16.0f;
constexpr std::int32_t kAutoHideTriggerRegionPx = 4;
float resolvedCapsuleRadius(const WidgetBarCapsuleSpec& spec, float scale, float width, float height) noexcept {
const float maxRadius = std::max(0.0f, std::min(width, height) * 0.5f);
if (!spec.radius.has_value()) {
return maxRadius;
}
return std::clamp(*spec.radius * scale, 0.0f, maxRadius);
}
bool pointInsideNode(const Node* node, float sceneX, float sceneY) {
if (node == nullptr) {
return false;
@@ -476,7 +468,9 @@ namespace {
bg->setPosition(0.0f, 0.0f);
bg->setSize(shellW, shellH);
content->setPosition(contentX, contentY);
bg->setRadius(resolvedCapsuleRadius(run.spec, scale, shellW, shellH));
const Widget* radiusSource = !run.widgets.empty() ? run.widgets.front() : nullptr;
bg->setRadius(radiusSource != nullptr ? radiusSource->resolvedBarCapsuleRadius(shellW, shellH)
: std::max(0.0f, std::min(shellW, shellH) * 0.5f));
}
};
finalizeCapsules(instance.startCapsuleRuns);
+10
View File
@@ -4,6 +4,8 @@
#include "ui/controls/box.h"
#include "ui/palette.h"
#include <algorithm>
namespace {
constexpr float kCapsuleInkEpsilon = 0.5f;
@@ -41,6 +43,14 @@ bool Widget::shouldShowBarCapsule() const {
return true;
}
float Widget::resolvedBarCapsuleRadius(float width, float height) const noexcept {
const float maxRadius = std::max(0.0f, std::min(width, height) * 0.5f);
if (!m_barCapsuleSpec.radius.has_value()) {
return maxRadius;
}
return std::clamp(*m_barCapsuleSpec.radius * m_contentScale, 0.0f, maxRadius);
}
void Widget::setBarCapsuleScene(Node* shell, Box* box) noexcept {
m_capsuleShell = shell;
m_capsuleBox = box;
+1
View File
@@ -69,6 +69,7 @@ public:
[[nodiscard]] Box* barCapsuleBox() const noexcept { return m_capsuleBox; }
// Outermost node for flex layout / anchor alignment (capsule shell when enabled).
[[nodiscard]] Node* layoutBoundsNode() const noexcept { return m_capsuleShell != nullptr ? m_capsuleShell : root(); }
[[nodiscard]] float resolvedBarCapsuleRadius(float width, float height) const noexcept;
// Whether the bar should paint the decorative capsule for this frame (spec enabled + visible ink).
[[nodiscard]] virtual bool shouldShowBarCapsule() const;
+1 -2
View File
@@ -230,7 +230,6 @@ void TaskbarWidget::buildTaskButtons(Renderer& renderer) {
};
if (m_groupByWorkspace && !m_workspaces.empty()) {
const float capsuleRadius = Style::radiusLg * m_contentScale;
const float groupGap = Style::spaceXs * m_contentScale;
const float groupPadCross = Style::spaceXs * 0.35f * m_contentScale;
const float groupPadEnd = Style::spaceXs * 0.55f * m_contentScale;
@@ -270,7 +269,7 @@ void TaskbarWidget::buildTaskButtons(Renderer& renderer) {
group->setFrameSize(groupWidth, groupHeight);
group->setFill(colorSpecFromRole(ColorRole::SurfaceVariant, ws.workspace.active ? 0.52f : 0.18f));
group->setBorder(colorSpecFromRole(ColorRole::Primary, ws.workspace.active ? 0.65f : 0.16f), Style::borderWidth);
group->setRadius(capsuleRadius);
group->setRadius(resolvedBarCapsuleRadius(groupWidth, groupHeight));
auto* groupPtr = static_cast<Box*>(m_taskStrip->addChild(std::move(group)));
if (tasks.empty()) {
+11 -4
View File
@@ -598,10 +598,17 @@ namespace settings {
if (!spec.visibleWhen.has_value()) {
return true;
}
const auto& cond = *spec.visibleWhen;
const auto currentValue = settingCurrentString(cfg, widgetName, cond.key, allSpecs);
for (const auto& v : cond.values) {
if (v == currentValue) {
auto matches = [&](const std::string& key, const std::vector<std::string>& values) {
const auto currentValue = settingCurrentString(cfg, widgetName, key, allSpecs);
for (const auto& v : values) {
if (v == currentValue) {
return true;
}
}
return false;
};
for (const auto& condition : spec.visibleWhen->any) {
if (matches(condition.key, condition.values)) {
return true;
}
}
@@ -413,6 +413,16 @@ namespace settings {
} else if (type == "taskbar") {
add(boolSpec("group_by_workspace", false));
add(boolSpec("show_all_outputs", false));
for (auto& spec : specs) {
if (spec.key == "capsule_radius") {
spec.descriptionKey = "settings.widgets.settings.capsule_radius.taskbar-description";
spec.visibleWhen = WidgetSettingVisibility{
WidgetSettingVisibilityCondition{"capsule", {"true"}},
WidgetSettingVisibilityCondition{"group_by_workspace", {"true"}},
};
break;
}
}
} else if (type == "tray") {
add(stringListSpec("hidden"));
add(stringListSpec("pinned"));
+12 -1
View File
@@ -3,9 +3,11 @@
#include "config/config_service.h"
#include <cstdint>
#include <initializer_list>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace settings {
@@ -53,11 +55,20 @@ namespace settings {
std::string_view labelKey;
};
struct WidgetSettingVisibility {
struct WidgetSettingVisibilityCondition {
std::string key;
std::vector<std::string> values;
};
struct WidgetSettingVisibility {
std::vector<WidgetSettingVisibilityCondition> any;
WidgetSettingVisibility() = default;
WidgetSettingVisibility(std::string key, std::vector<std::string> values)
: any{WidgetSettingVisibilityCondition{std::move(key), std::move(values)}} {}
WidgetSettingVisibility(std::initializer_list<WidgetSettingVisibilityCondition> alternatives) : any(alternatives) {}
};
struct WidgetSettingSpec {
std::string key;
std::string labelKey;