feat(widgets): add input & output option to volume widget

This commit is contained in:
Ly-sec
2026-05-07 03:07:38 +02:00
parent 9ccfc7fd38
commit 152d0b6fad
6 changed files with 82 additions and 15 deletions
+14
View File
@@ -831,6 +831,14 @@
}
},
"widgets": {
"instances": {
"cpu": "CPU",
"date": "Date",
"input-volume": "Input Volume",
"output-volume": "Output Volume",
"ram": "RAM",
"temp": "Temperature"
},
"categories": {
"custom": "Custom",
"info": "Info",
@@ -883,9 +891,11 @@
"gauge": "Gauge",
"graph": "Graph",
"id": "ID",
"input": "Input",
"name": "Name",
"none": "None",
"on-hover": "On Hover",
"output": "Output",
"ram-percent": "RAM Percent",
"ram-used": "RAM Used",
"short": "Short",
@@ -946,6 +956,10 @@
"label": "Cycle Command",
"description": "Command run when cycling keyboard layouts"
},
"device": {
"label": "Device",
"description": "Audio stream to control"
},
"display": {
"label": "Display",
"description": "Display mode for this widget"
+10
View File
@@ -729,6 +729,16 @@ void ConfigService::seedBuiltinWidgets(Config& config) {
ram.settings["stat"] = std::string("ram_used");
seed("ram", std::move(ram));
WidgetConfig outputVolume;
outputVolume.type = "volume";
outputVolume.settings["device"] = std::string("output");
seed("output_volume", std::move(outputVolume));
WidgetConfig inputVolume;
inputVolume.type = "volume";
inputVolume.settings["device"] = std::string("input");
seed("input_volume", std::move(inputVolume));
WidgetConfig date;
date.type = "clock";
date.settings["format"] = std::string("{:%a %d %b}");
+3 -1
View File
@@ -356,7 +356,9 @@ std::unique_ptr<Widget> WidgetFactory::create(const std::string& name, wl_output
if (type == "volume") {
const bool showLabel = wc != nullptr ? wc->getBool("show_label", true) : true;
auto widget = std::make_unique<VolumeWidget>(m_audio, output, showLabel);
const std::string target = wc != nullptr ? wc->getString("device", "output") : std::string("output");
const auto volumeTarget = target == "input" ? VolumeWidgetTarget::Input : VolumeWidgetTarget::Output;
auto widget = std::make_unique<VolumeWidget>(m_audio, output, showLabel, volumeTarget);
widget->setContentScale(contentScale);
return widget;
}
+19 -11
View File
@@ -15,7 +15,11 @@
namespace {
const char* volumeGlyphName(float volume, bool muted) {
const char* volumeGlyphName(float volume, bool muted, VolumeWidgetTarget target) {
if (target == VolumeWidgetTarget::Input) {
return muted ? "microphone-mute" : "microphone";
}
if (muted || volume <= 0.0f) {
return "volume-mute";
}
@@ -29,8 +33,8 @@ namespace {
} // namespace
VolumeWidget::VolumeWidget(PipeWireService* audio, wl_output* output, bool showLabel)
: m_audio(audio), m_output(output), m_showLabel(showLabel) {}
VolumeWidget::VolumeWidget(PipeWireService* audio, wl_output* output, bool showLabel, VolumeWidgetTarget target)
: m_audio(audio), m_output(output), m_showLabel(showLabel), m_target(target) {}
void VolumeWidget::create() {
auto area = std::make_unique<InputArea>();
@@ -39,13 +43,17 @@ void VolumeWidget::create() {
if (m_audio == nullptr) {
return;
}
const auto* sink = m_audio->defaultSink();
if (sink == nullptr) {
const auto* node = m_target == VolumeWidgetTarget::Input ? m_audio->defaultSource() : m_audio->defaultSink();
if (node == nullptr) {
return;
}
const float delta = data.scrollDelta(1.0f) > 0 ? -kScrollStep : kScrollStep;
const float newValue = std::clamp(sink->volume + delta, 0.0f, 1.0f);
m_audio->setSinkVolume(sink->id, newValue);
const float newValue = std::clamp(node->volume + delta, 0.0f, 1.0f);
if (m_target == VolumeWidgetTarget::Input) {
m_audio->setSourceVolume(node->id, newValue);
} else {
m_audio->setSinkVolume(node->id, newValue);
}
});
auto glyph = std::make_unique<Glyph>();
@@ -103,9 +111,9 @@ void VolumeWidget::syncState(Renderer& renderer) {
return;
}
const auto* sink = m_audio->defaultSink();
float volume = sink != nullptr ? sink->volume : 0.0f;
bool muted = sink != nullptr ? sink->muted : false;
const auto* node = m_target == VolumeWidgetTarget::Input ? m_audio->defaultSource() : m_audio->defaultSink();
float volume = node != nullptr ? node->volume : 0.0f;
bool muted = node != nullptr ? node->muted : false;
if (volume == m_lastVolume && muted == m_lastMuted && m_isVertical == m_lastVertical) {
return;
@@ -115,7 +123,7 @@ void VolumeWidget::syncState(Renderer& renderer) {
m_lastMuted = muted;
m_lastVertical = m_isVertical;
m_glyph->setGlyph(volumeGlyphName(volume, muted));
m_glyph->setGlyph(volumeGlyphName(volume, muted, m_target));
m_glyph->setGlyphSize(Style::barGlyphSize * m_contentScale);
m_glyph->setColor(muted ? colorSpecFromRole(ColorRole::OnSurfaceVariant)
: widgetForegroundOr(colorSpecFromRole(ColorRole::OnSurface)));
+7 -1
View File
@@ -9,9 +9,14 @@ class Label;
class PipeWireService;
struct wl_output;
enum class VolumeWidgetTarget {
Output,
Input,
};
class VolumeWidget : public Widget {
public:
VolumeWidget(PipeWireService* audio, wl_output* output, bool showLabel);
VolumeWidget(PipeWireService* audio, wl_output* output, bool showLabel, VolumeWidgetTarget target);
void create() override;
@@ -23,6 +28,7 @@ private:
PipeWireService* m_audio = nullptr;
wl_output* m_output = nullptr;
bool m_showLabel = true;
VolumeWidgetTarget m_target = VolumeWidgetTarget::Output;
Glyph* m_glyph = nullptr;
Label* m_label = nullptr;
float m_lastVolume = -1.0f;
@@ -238,6 +238,28 @@ namespace settings {
return spec;
}
std::string widgetInstanceDisplayLabel(std::string_view name) {
if (name == "cpu") {
return tr("settings.widgets.instances.cpu");
}
if (name == "temp") {
return tr("settings.widgets.instances.temp");
}
if (name == "ram") {
return tr("settings.widgets.instances.ram");
}
if (name == "date") {
return tr("settings.widgets.instances.date");
}
if (name == "output_volume") {
return tr("settings.widgets.instances.output-volume");
}
if (name == "input_volume") {
return tr("settings.widgets.instances.input-volume");
}
return std::string(name);
}
void addPickerEntry(std::vector<WidgetPickerEntry>& entries, std::unordered_set<std::string>& seen,
std::string value, std::string label, std::string description, std::string category,
WidgetReferenceKind kind) {
@@ -317,7 +339,7 @@ namespace settings {
if (const auto it = cfg.widgets.find(std::string(name)); it != cfg.widgets.end()) {
return WidgetReferenceInfo{
.title = std::string(name),
.title = widgetInstanceDisplayLabel(name),
.detail = it->second.type.empty() ? tr("settings.entities.widget.detail.custom")
: tr("settings.entities.widget.detail.type", "type", it->second.type),
.badge = tr("settings.entities.widget.kinds.named"),
@@ -349,7 +371,7 @@ namespace settings {
if (isBuiltInWidgetType(name)) {
continue;
}
addPickerEntry(entries, seen, name, name,
addPickerEntry(entries, seen, name, widgetInstanceDisplayLabel(name),
widget.type.empty() ? tr("settings.entities.widget.detail.custom")
: tr("settings.entities.widget.detail.type", "type", widget.type),
tr("settings.entities.widget.kinds.named"), WidgetReferenceKind::Named);
@@ -423,6 +445,10 @@ namespace settings {
{"always", "settings.widgets.options.always"},
{"on_hover", "settings.widgets.options.on-hover"},
};
const std::vector<WidgetSettingSelectOption> volumeDeviceOptions = {
{"output", "settings.widgets.options.output"},
{"input", "settings.widgets.options.input"},
};
const std::vector<WidgetSettingSelectOption> workspaceColorRoles = {
{"on_surface", ""}, {"primary", ""}, {"secondary", ""}, {"tertiary", ""}, {"error", ""},
};
@@ -494,6 +520,7 @@ namespace settings {
add(boolSpec("drawer", false));
add(intSpec("drawer_columns", 3, 1.0, 5.0, 1.0));
} else if (type == "volume") {
add(segmentedSpec("device", "output", volumeDeviceOptions));
add(boolSpec("show_label", true));
} else if (type == "wallpaper") {
add(stringSpec("glyph", "wallpaper-selector"));