Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion src/shell/bar/widget_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -646,12 +646,45 @@ std::unique_ptr<Widget> WidgetFactory::create(
} else if (display == "none") {
displayMode = WorkspacesWidget::DisplayMode::None;
}

const std::string colorModeValue = wc != nullptr ? wc->getString("color_mode", "state") : std::string("state");

WorkspacesWidget::ColorMode colorMode = WorkspacesWidget::ColorMode::State;

if (colorModeValue == "cycle") {
colorMode = WorkspacesWidget::ColorMode::Cycle;
} else if (colorModeValue != "state") {
kLog.warn("invalid widget.{}.color_mode '{}'; expected state or cycle", name, colorModeValue);
}

std::vector<ColorSpec> cycleColors;

if (wc != nullptr) {
const auto configuredColors = wc->getStringList("cycle_colors");

cycleColors.reserve(configuredColors.size());

for (std::size_t i = 0; i < configuredColors.size(); ++i) {
cycleColors.push_back(colorSpecFromConfigString(
configuredColors[i], "widget." + name + ".cycle_colors[" + std::to_string(i) + "]"
));
}
}

if (colorMode == WorkspacesWidget::ColorMode::Cycle && cycleColors.empty()) {
kLog.warn("widget.{}.color_mode is cycle but cycle_colors is empty; falling back to state", name);

colorMode = WorkspacesWidget::ColorMode::State;
}

std::size_t maxLabelChars = 1; // Default: truncate names to 1 char (v4 behavior)
if (wc != nullptr && wc->hasSetting("max_label_chars")) {
maxLabelChars = static_cast<std::size_t>(wc->getInt("max_label_chars", 1));
}
WorkspacesWidget::Options options{
.displayMode = displayMode,
.colorMode = colorMode,
.cycleColors = std::move(cycleColors),
.focusedColor = focusedColor,
.occupiedColor = occupiedColor,
.emptyColor = emptyColor,
Expand All @@ -664,7 +697,7 @@ std::unique_ptr<Widget> WidgetFactory::create(
.minimal = wc != nullptr ? wc->getBool("minimal", false) : false,
.focusedOutputOnly = wc != nullptr ? wc->getBool("focused_output_only", false) : false,
};
auto widget = std::make_unique<WorkspacesWidget>(m_platform, output, options);
auto widget = std::make_unique<WorkspacesWidget>(m_platform, output, std::move(options));
widget->setContentScale(contentScale);
return widget;
}
Expand Down
48 changes: 44 additions & 4 deletions src/shell/bar/widgets/workspaces_widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ namespace {
} // namespace

WorkspacesWidget::WorkspacesWidget(CompositorPlatform& platform, wl_output* output, Options options)
: m_platform(platform), m_output(output), m_displayMode(options.displayMode),
m_maxLabelChars(options.maxLabelChars), m_labelsOnlyWhenOccupied(options.labelsOnlyWhenOccupied),
m_hideWhenEmpty(options.hideWhenEmpty), m_pillScale(options.pillScale),
m_activePillSize(std::clamp(options.activePillSize, 0.25f, 8.0f)),
: m_platform(platform), m_output(output), m_displayMode(options.displayMode), m_colorMode(options.colorMode),
m_cycleColors(std::move(options.cycleColors)), m_maxLabelChars(options.maxLabelChars),
m_labelsOnlyWhenOccupied(options.labelsOnlyWhenOccupied), m_hideWhenEmpty(options.hideWhenEmpty),
m_pillScale(options.pillScale), m_activePillSize(std::clamp(options.activePillSize, 0.25f, 8.0f)),
m_inactivePillSize(std::clamp(options.inactivePillSize, 0.25f, 8.0f)), m_minimal(options.minimal),
m_focusedOutputOnly(options.focusedOutputOnly), m_focusedColor(options.focusedColor),
m_occupiedColor(options.occupiedColor), m_emptyColor(options.emptyColor) {}
Expand Down Expand Up @@ -708,21 +708,48 @@ std::optional<std::size_t> WorkspacesWidget::numericWorkspaceId(const Workspace&
return std::nullopt;
}

ColorSpec WorkspacesWidget::workspaceCycleColor(const Workspace& workspace) const {
if (m_cycleColors.empty()) {
return m_emptyColor;
}

std::size_t workspaceNumber = workspace.index;

if (workspaceNumber == 0) {
workspaceNumber = numericWorkspaceId(workspace).value_or(1);
}

const std::size_t colorIndex = (workspaceNumber - 1) % m_cycleColors.size();
return m_cycleColors[colorIndex];
}

bool WorkspacesWidget::isFocusedOutput() const { return m_platform.preferredInteractiveOutput() == m_output; }

ColorSpec WorkspacesWidget::workspaceFillColor(const Workspace& workspace) const {
if (workspace.active) {
if (m_colorMode == ColorMode::Cycle && !m_cycleColors.empty()) {
return workspaceCycleColor(workspace);
}

if (m_activeUsesFocusedColor) {
return m_focusedColor;
}

return m_occupiedColor;
}

if (workspace.urgent) {
return colorSpecFromRole(ColorRole::Error);
}

if (workspace.occupied) {
if (m_colorMode == ColorMode::Cycle && !m_cycleColors.empty()) {
return workspaceCycleColor(workspace);
}

return m_occupiedColor;
}

ColorSpec color = m_emptyColor;
color.alpha *= 0.55f;
return color;
Expand All @@ -732,18 +759,31 @@ ColorSpec WorkspacesWidget::workspaceTextColor(const Workspace& workspace) const
if (workspace.urgent) {
return m_minimal ? colorSpecFromRole(ColorRole::Error) : colorSpecFromRole(ColorRole::OnError);
}

if (!m_minimal) {
return readableColorForFill(workspaceFillColor(workspace));
}

if (workspace.active) {
if (m_colorMode == ColorMode::Cycle && !m_cycleColors.empty()) {
return workspaceCycleColor(workspace);
}

if (m_activeUsesFocusedColor) {
return m_focusedColor;
}

return m_occupiedColor;
}

if (workspace.occupied) {
if (m_colorMode == ColorMode::Cycle && !m_cycleColors.empty()) {
return workspaceCycleColor(workspace);
}

return m_occupiedColor;
}

ColorSpec color = widgetForegroundOr(colorSpecFromRole(ColorRole::OnSurfaceVariant));
color.alpha *= 0.55f;
return color;
Expand Down
10 changes: 10 additions & 0 deletions src/shell/bar/widgets/workspaces_widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,15 @@ class WorkspacesWidget : public Widget {
Name,
};

enum class ColorMode : std::uint8_t {
State,
Cycle,
};

struct Options {
DisplayMode displayMode = DisplayMode::Id;
ColorMode colorMode = ColorMode::State;
std::vector<ColorSpec> cycleColors;
ColorSpec focusedColor = colorSpecFromRole(ColorRole::Primary);
ColorSpec occupiedColor = colorSpecFromRole(ColorRole::Secondary);
ColorSpec emptyColor = colorSpecFromRole(ColorRole::Secondary);
Expand Down Expand Up @@ -86,6 +93,7 @@ class WorkspacesWidget : public Widget {
};

[[nodiscard]] ColorSpec workspaceFillColor(const Workspace& workspace) const;
[[nodiscard]] ColorSpec workspaceCycleColor(const Workspace& workspace) const;
[[nodiscard]] ColorSpec workspaceTextColor(const Workspace& workspace) const;
[[nodiscard]] static ColorRole onRoleForFill(ColorRole fill);
[[nodiscard]] static ColorSpec readableColorForFill(const ColorSpec& fill);
Expand All @@ -94,6 +102,8 @@ class WorkspacesWidget : public Widget {
CompositorPlatform& m_platform;
wl_output* m_output = nullptr;
DisplayMode m_displayMode = DisplayMode::None;
ColorMode m_colorMode = ColorMode::State;
std::vector<ColorSpec> m_cycleColors;
std::size_t m_maxLabelChars = 1;
bool m_labelsOnlyWhenOccupied = false;
bool m_hideWhenEmpty = false;
Expand Down
8 changes: 8 additions & 0 deletions src/shell/settings/widget_settings_registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,14 @@ namespace settings {
add(std::move(focusedOutputOnly));
}
add(segmentedSpec("display", "id", workspaceDisplay));
{
auto colorMode = stringSpec("color_mode", "state", true);
add(std::move(colorMode));
}
{
auto cycleColors = stringListSpec("cycle_colors", {}, true);
add(std::move(cycleColors));
}
{
auto labelsOnlyWhenOccupied = boolSpec("labels_only_when_occupied", false);
labelsOnlyWhenOccupied.descriptionKey =
Expand Down