feat(wlr/taskbar): expose active toplevel state on the bar window
Add opt-in bar-css-states option to wlr/taskbar module. When enabled, the module tracks the active workspace and updates CSS classes on window#waybar to reflect the current window state: - toplevel-active: when a window is focused - toplevel-maximized: when any non-minimized window on active workspace is maximized - toplevel-minimized: when the active window is minimized - toplevel-fullscreen: when any non-minimized window on active workspace is fullscreen This feature requires compositor support for ext-workspace-v1 protocol to correctly track workspace membership. Without it, classes fall back to the active application state only. Also includes active-only option implementation. Changes: - Add ext-workspace-v1 protocol support for workspace tracking - Implement WorkspaceState tracking in Taskbar class - Add workspace management lifecycle (create/remove/done) - Add bar CSS class update logic with aggregation - Add visible() getter to Task class - Implement active-only task visibility control - Update man page with documentation and CSS examples Signed-off-by: iamcheyan <iamcheyan@users.noreply.github.com>
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
#include "AModule.hpp"
|
#include "AModule.hpp"
|
||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
|
#include "ext-workspace-v1-client-protocol.h"
|
||||||
#include "giomm/desktopappinfo.h"
|
#include "giomm/desktopappinfo.h"
|
||||||
#include "util/icon_loader.hpp"
|
#include "util/icon_loader.hpp"
|
||||||
#include "util/json.hpp"
|
#include "util/json.hpp"
|
||||||
@@ -80,6 +81,7 @@ class Task {
|
|||||||
std::string title_;
|
std::string title_;
|
||||||
std::string app_id_;
|
std::string app_id_;
|
||||||
uint32_t state_ = 0;
|
uint32_t state_ = 0;
|
||||||
|
struct ext_workspace_handle_v1* workspace_ = nullptr;
|
||||||
|
|
||||||
int32_t drag_start_x;
|
int32_t drag_start_x;
|
||||||
int32_t drag_start_y;
|
int32_t drag_start_y;
|
||||||
@@ -102,6 +104,9 @@ class Task {
|
|||||||
bool minimized() const { return state_ & MINIMIZED; }
|
bool minimized() const { return state_ & MINIMIZED; }
|
||||||
bool active() const { return state_ & ACTIVE; }
|
bool active() const { return state_ & ACTIVE; }
|
||||||
bool fullscreen() const { return state_ & FULLSCREEN; }
|
bool fullscreen() const { return state_ & FULLSCREEN; }
|
||||||
|
bool visible() const { return button_visible_; }
|
||||||
|
struct ext_workspace_handle_v1* workspace() const { return workspace_; }
|
||||||
|
void set_workspace(struct ext_workspace_handle_v1* workspace) { workspace_ = workspace; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* Callbacks for the wlr protocol */
|
/* Callbacks for the wlr protocol */
|
||||||
@@ -142,6 +147,12 @@ using TaskPtr = std::unique_ptr<Task>;
|
|||||||
|
|
||||||
class Taskbar : public waybar::AModule {
|
class Taskbar : public waybar::AModule {
|
||||||
public:
|
public:
|
||||||
|
struct WorkspaceState {
|
||||||
|
Taskbar* taskbar;
|
||||||
|
struct ext_workspace_handle_v1* handle;
|
||||||
|
uint32_t state = 0;
|
||||||
|
};
|
||||||
|
|
||||||
Taskbar(const std::string&, const waybar::Bar&, const Json::Value&);
|
Taskbar(const std::string&, const waybar::Bar&, const Json::Value&);
|
||||||
~Taskbar();
|
~Taskbar();
|
||||||
void update();
|
void update();
|
||||||
@@ -156,22 +167,35 @@ class Taskbar : public waybar::AModule {
|
|||||||
std::map<std::string, std::string> app_ids_replace_map_;
|
std::map<std::string, std::string> app_ids_replace_map_;
|
||||||
|
|
||||||
struct zwlr_foreign_toplevel_manager_v1* manager_;
|
struct zwlr_foreign_toplevel_manager_v1* manager_;
|
||||||
|
struct ext_workspace_manager_v1* workspace_manager_;
|
||||||
struct wl_seat* seat_;
|
struct wl_seat* seat_;
|
||||||
|
std::vector<struct ext_workspace_group_handle_v1*> workspace_groups_;
|
||||||
|
std::vector<std::unique_ptr<WorkspaceState>> workspaces_;
|
||||||
|
struct ext_workspace_handle_v1* current_workspace_ = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* Callbacks for global registration */
|
/* Callbacks for global registration */
|
||||||
void register_manager(struct wl_registry*, uint32_t name, uint32_t version);
|
void register_manager(struct wl_registry*, uint32_t name, uint32_t version);
|
||||||
|
void register_workspace_manager(struct wl_registry*, uint32_t name, uint32_t version);
|
||||||
void register_seat(struct wl_registry*, uint32_t name, uint32_t version);
|
void register_seat(struct wl_registry*, uint32_t name, uint32_t version);
|
||||||
|
|
||||||
/* Callbacks for the wlr protocol */
|
/* Callbacks for the wlr protocol */
|
||||||
void handle_toplevel_create(struct zwlr_foreign_toplevel_handle_v1*);
|
void handle_toplevel_create(struct zwlr_foreign_toplevel_handle_v1*);
|
||||||
void handle_finished();
|
void handle_finished();
|
||||||
|
void handle_workspace_group_create(struct ext_workspace_group_handle_v1*);
|
||||||
|
void handle_workspace_group_removed(struct ext_workspace_group_handle_v1*);
|
||||||
|
void handle_workspace_create(struct ext_workspace_handle_v1*);
|
||||||
|
void handle_workspace_done();
|
||||||
|
void handle_workspace_finished();
|
||||||
|
void handle_workspace_removed(struct ext_workspace_handle_v1*);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void add_button(Gtk::Button&);
|
void add_button(Gtk::Button&);
|
||||||
void move_button(Gtk::Button&, int);
|
void move_button(Gtk::Button&, int);
|
||||||
void remove_button(Gtk::Button&);
|
void remove_button(Gtk::Button&);
|
||||||
void remove_task(uint32_t);
|
void remove_task(uint32_t);
|
||||||
|
void assign_current_workspace(Task&);
|
||||||
|
void update_bar_css_classes();
|
||||||
|
|
||||||
bool show_output(struct wl_output*) const;
|
bool show_output(struct wl_output*) const;
|
||||||
bool all_outputs() const;
|
bool all_outputs() const;
|
||||||
@@ -179,6 +203,9 @@ class Taskbar : public waybar::AModule {
|
|||||||
const IconLoader& icon_loader() const;
|
const IconLoader& icon_loader() const;
|
||||||
const std::unordered_set<std::string>& ignore_list() const;
|
const std::unordered_set<std::string>& ignore_list() const;
|
||||||
const std::map<std::string, std::string>& app_ids_replace_map() const;
|
const std::map<std::string, std::string>& app_ids_replace_map() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void set_bar_css_class(const std::string&, bool);
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace waybar::modules::wlr */
|
} /* namespace waybar::modules::wlr */
|
||||||
|
|||||||
@@ -18,6 +18,13 @@ Addressed by *wlr/taskbar*
|
|||||||
default: false ++
|
default: false ++
|
||||||
If set to false applications on the waybar's current output will be shown. Otherwise, all applications are shown.
|
If set to false applications on the waybar's current output will be shown. Otherwise, all applications are shown.
|
||||||
|
|
||||||
|
*bar-css-states*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
If set to true, application state is exposed as CSS classes on the Waybar
|
||||||
|
window. Maximized and fullscreen state is aggregated across applications
|
||||||
|
known to belong to the active workspace. See *Bar state style* below.
|
||||||
|
|
||||||
*format*: ++
|
*format*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: {icon} ++
|
default: {icon} ++
|
||||||
@@ -52,6 +59,12 @@ Addressed by *wlr/taskbar*
|
|||||||
default: false ++
|
default: false ++
|
||||||
If set to true, always reorder the tasks in the taskbar so that the currently active one is first. Otherwise don't reorder.
|
If set to true, always reorder the tasks in the taskbar so that the currently active one is first. Otherwise don't reorder.
|
||||||
|
|
||||||
|
*active-only*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
If set to true, only the currently active application button is shown.
|
||||||
|
Other applications remain tracked and reappear when activated.
|
||||||
|
|
||||||
*sort-by-app-id*: ++
|
*sort-by-app-id*: ++
|
||||||
typeof: bool ++
|
typeof: bool ++
|
||||||
default: false ++
|
default: false ++
|
||||||
@@ -156,3 +169,35 @@ Invalid expressions (e.g., mismatched parentheses) are skipped.
|
|||||||
- *#taskbar button.minimized*
|
- *#taskbar button.minimized*
|
||||||
- *#taskbar button.active*
|
- *#taskbar button.active*
|
||||||
- *#taskbar button.fullscreen*
|
- *#taskbar button.fullscreen*
|
||||||
|
|
||||||
|
# Bar state style
|
||||||
|
|
||||||
|
When *bar-css-states* is enabled, the following classes are added to
|
||||||
|
*window#waybar*:
|
||||||
|
|
||||||
|
- *window#waybar.toplevel-active*
|
||||||
|
- *window#waybar.toplevel-maximized*
|
||||||
|
- *window#waybar.toplevel-minimized*
|
||||||
|
- *window#waybar.toplevel-fullscreen*
|
||||||
|
|
||||||
|
The active, minimized classes describe the active application. The maximized
|
||||||
|
and fullscreen classes are set if any non-minimized application known to belong
|
||||||
|
to the active workspace has that state.
|
||||||
|
|
||||||
|
Workspace membership is learned when an application is activated and requires
|
||||||
|
the compositor to support *ext-workspace-v1*. Before an application has been
|
||||||
|
activated during the current Waybar session, its workspace may be unknown. On
|
||||||
|
compositors without *ext-workspace-v1*, these classes fall back to the active
|
||||||
|
application's state.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
window#waybar {
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
window#waybar.toplevel-maximized {
|
||||||
|
background-color: rgba(0, 0, 0, 1);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
+244
-1
@@ -293,9 +293,12 @@ void Task::handle_output_enter(struct wl_output* output) {
|
|||||||
button.signal_size_allocate().connect_notify(
|
button.signal_size_allocate().connect_notify(
|
||||||
sigc::mem_fun(this, &Task::on_button_size_allocated));
|
sigc::mem_fun(this, &Task::on_button_size_allocated));
|
||||||
tbar_->add_button(button);
|
tbar_->add_button(button);
|
||||||
button.show();
|
if (!config_["active-only"].asBool() || active()) {
|
||||||
|
button.show();
|
||||||
|
}
|
||||||
button_visible_ = true;
|
button_visible_ = true;
|
||||||
spdlog::debug("{} now visible on {}", repr(), bar_.output->name);
|
spdlog::debug("{} now visible on {}", repr(), bar_.output->name);
|
||||||
|
tbar_->update_bar_css_classes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,6 +311,7 @@ void Task::handle_output_leave(struct wl_output* output) {
|
|||||||
button.hide();
|
button.hide();
|
||||||
button_visible_ = false;
|
button_visible_ = false;
|
||||||
spdlog::debug("{} now invisible on {}", repr(), bar_.output->name);
|
spdlog::debug("{} now invisible on {}", repr(), bar_.output->name);
|
||||||
|
tbar_->update_bar_css_classes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,6 +354,20 @@ void Task::handle_done() {
|
|||||||
button.get_style_context()->remove_class("fullscreen");
|
button.get_style_context()->remove_class("fullscreen");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (button_visible_ && config_["active-only"].asBool()) {
|
||||||
|
if (active()) {
|
||||||
|
button.show();
|
||||||
|
} else {
|
||||||
|
button.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active()) {
|
||||||
|
tbar_->assign_current_workspace(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
tbar_->update_bar_css_classes();
|
||||||
|
|
||||||
if (config_["active-first"].isBool() && config_["active-first"].asBool() && active())
|
if (config_["active-first"].isBool() && config_["active-first"].asBool() && active())
|
||||||
tbar_->move_button(button, 0);
|
tbar_->move_button(button, 0);
|
||||||
|
|
||||||
@@ -551,6 +569,8 @@ static void handle_global(void* data, struct wl_registry* registry, uint32_t nam
|
|||||||
const char* interface, uint32_t version) {
|
const char* interface, uint32_t version) {
|
||||||
if (std::strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0) {
|
if (std::strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0) {
|
||||||
static_cast<Taskbar*>(data)->register_manager(registry, name, version);
|
static_cast<Taskbar*>(data)->register_manager(registry, name, version);
|
||||||
|
} else if (std::strcmp(interface, ext_workspace_manager_v1_interface.name) == 0) {
|
||||||
|
static_cast<Taskbar*>(data)->register_workspace_manager(registry, name, version);
|
||||||
} else if (std::strcmp(interface, wl_seat_interface.name) == 0) {
|
} else if (std::strcmp(interface, wl_seat_interface.name) == 0) {
|
||||||
static_cast<Taskbar*>(data)->register_seat(registry, name, version);
|
static_cast<Taskbar*>(data)->register_seat(registry, name, version);
|
||||||
}
|
}
|
||||||
@@ -568,6 +588,7 @@ Taskbar::Taskbar(const std::string& id, const waybar::Bar& bar, const Json::Valu
|
|||||||
bar_(bar),
|
bar_(bar),
|
||||||
box_{bar.orientation, 0},
|
box_{bar.orientation, 0},
|
||||||
manager_{nullptr},
|
manager_{nullptr},
|
||||||
|
workspace_manager_{nullptr},
|
||||||
seat_{nullptr} {
|
seat_{nullptr} {
|
||||||
box_.set_name("taskbar");
|
box_.set_name("taskbar");
|
||||||
if (!id.empty()) {
|
if (!id.empty()) {
|
||||||
@@ -623,6 +644,27 @@ Taskbar::Taskbar(const std::string& id, const waybar::Bar& bar, const Json::Valu
|
|||||||
}
|
}
|
||||||
|
|
||||||
Taskbar::~Taskbar() {
|
Taskbar::~Taskbar() {
|
||||||
|
for (auto& workspace : workspaces_) {
|
||||||
|
ext_workspace_handle_v1_destroy(workspace->handle);
|
||||||
|
}
|
||||||
|
workspaces_.clear();
|
||||||
|
for (auto* group : workspace_groups_) {
|
||||||
|
ext_workspace_group_handle_v1_destroy(group);
|
||||||
|
}
|
||||||
|
workspace_groups_.clear();
|
||||||
|
|
||||||
|
if (workspace_manager_) {
|
||||||
|
struct wl_display* display = Client::inst()->wl_display;
|
||||||
|
ext_workspace_manager_v1_stop(workspace_manager_);
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
if (workspace_manager_) {
|
||||||
|
spdlog::warn("Workspace manager destroyed before .finished event");
|
||||||
|
ext_workspace_manager_v1_destroy(workspace_manager_);
|
||||||
|
workspace_manager_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (manager_) {
|
if (manager_) {
|
||||||
struct wl_display* display = Client::inst()->wl_display;
|
struct wl_display* display = Client::inst()->wl_display;
|
||||||
/*
|
/*
|
||||||
@@ -639,6 +681,13 @@ Taskbar::~Taskbar() {
|
|||||||
manager_ = nullptr;
|
manager_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config_["bar-css-states"].asBool()) {
|
||||||
|
set_bar_css_class("toplevel-active", false);
|
||||||
|
set_bar_css_class("toplevel-maximized", false);
|
||||||
|
set_bar_css_class("toplevel-minimized", false);
|
||||||
|
set_bar_css_class("toplevel-fullscreen", false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Taskbar::update() {
|
void Taskbar::update() {
|
||||||
@@ -674,6 +723,80 @@ static const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_i
|
|||||||
.finished = tm_handle_finished,
|
.finished = tm_handle_finished,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void workspace_handle_id(void*, struct ext_workspace_handle_v1*, const char*) {}
|
||||||
|
static void workspace_handle_name(void*, struct ext_workspace_handle_v1*, const char*) {}
|
||||||
|
static void workspace_handle_coordinates(void*, struct ext_workspace_handle_v1*, struct wl_array*) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_handle_state(void* data, struct ext_workspace_handle_v1*, uint32_t state) {
|
||||||
|
static_cast<Taskbar::WorkspaceState*>(data)->state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_handle_capabilities(void*, struct ext_workspace_handle_v1*, uint32_t) {}
|
||||||
|
|
||||||
|
static void workspace_handle_removed(void* data, struct ext_workspace_handle_v1* handle) {
|
||||||
|
static_cast<Taskbar::WorkspaceState*>(data)->taskbar->handle_workspace_removed(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ext_workspace_handle_v1_listener workspace_handle_impl = {
|
||||||
|
.id = workspace_handle_id,
|
||||||
|
.name = workspace_handle_name,
|
||||||
|
.coordinates = workspace_handle_coordinates,
|
||||||
|
.state = workspace_handle_state,
|
||||||
|
.capabilities = workspace_handle_capabilities,
|
||||||
|
.removed = workspace_handle_removed,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void workspace_group_handle_capabilities(void*, struct ext_workspace_group_handle_v1*,
|
||||||
|
uint32_t) {}
|
||||||
|
static void workspace_group_handle_output_enter(void*, struct ext_workspace_group_handle_v1*,
|
||||||
|
struct wl_output*) {}
|
||||||
|
static void workspace_group_handle_output_leave(void*, struct ext_workspace_group_handle_v1*,
|
||||||
|
struct wl_output*) {}
|
||||||
|
static void workspace_group_handle_workspace_enter(void*, struct ext_workspace_group_handle_v1*,
|
||||||
|
struct ext_workspace_handle_v1*) {}
|
||||||
|
static void workspace_group_handle_workspace_leave(void*, struct ext_workspace_group_handle_v1*,
|
||||||
|
struct ext_workspace_handle_v1*) {}
|
||||||
|
|
||||||
|
static void workspace_group_handle_removed(void* data,
|
||||||
|
struct ext_workspace_group_handle_v1* group) {
|
||||||
|
static_cast<Taskbar*>(data)->handle_workspace_group_removed(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ext_workspace_group_handle_v1_listener workspace_group_impl = {
|
||||||
|
.capabilities = workspace_group_handle_capabilities,
|
||||||
|
.output_enter = workspace_group_handle_output_enter,
|
||||||
|
.output_leave = workspace_group_handle_output_leave,
|
||||||
|
.workspace_enter = workspace_group_handle_workspace_enter,
|
||||||
|
.workspace_leave = workspace_group_handle_workspace_leave,
|
||||||
|
.removed = workspace_group_handle_removed,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void workspace_manager_handle_group(void* data, struct ext_workspace_manager_v1*,
|
||||||
|
struct ext_workspace_group_handle_v1* group) {
|
||||||
|
static_cast<Taskbar*>(data)->handle_workspace_group_create(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_manager_handle_workspace(void* data, struct ext_workspace_manager_v1*,
|
||||||
|
struct ext_workspace_handle_v1* workspace) {
|
||||||
|
static_cast<Taskbar*>(data)->handle_workspace_create(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_manager_handle_done(void* data, struct ext_workspace_manager_v1*) {
|
||||||
|
static_cast<Taskbar*>(data)->handle_workspace_done();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void workspace_manager_handle_finished(void* data, struct ext_workspace_manager_v1*) {
|
||||||
|
static_cast<Taskbar*>(data)->handle_workspace_finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ext_workspace_manager_v1_listener workspace_manager_impl = {
|
||||||
|
.workspace_group = workspace_manager_handle_group,
|
||||||
|
.workspace = workspace_manager_handle_workspace,
|
||||||
|
.done = workspace_manager_handle_done,
|
||||||
|
.finished = workspace_manager_handle_finished,
|
||||||
|
};
|
||||||
|
|
||||||
void Taskbar::register_manager(struct wl_registry* registry, uint32_t name, uint32_t version) {
|
void Taskbar::register_manager(struct wl_registry* registry, uint32_t name, uint32_t version) {
|
||||||
if (manager_) {
|
if (manager_) {
|
||||||
spdlog::warn("Register foreign toplevel manager again although already existing!");
|
spdlog::warn("Register foreign toplevel manager again although already existing!");
|
||||||
@@ -698,6 +821,18 @@ void Taskbar::register_manager(struct wl_registry* registry, uint32_t name, uint
|
|||||||
spdlog::debug("Failed to register manager");
|
spdlog::debug("Failed to register manager");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Taskbar::register_workspace_manager(struct wl_registry* registry, uint32_t name,
|
||||||
|
uint32_t version) {
|
||||||
|
if (workspace_manager_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
version = std::min<uint32_t>(version, ext_workspace_manager_v1_interface.version);
|
||||||
|
workspace_manager_ = static_cast<struct ext_workspace_manager_v1*>(
|
||||||
|
wl_registry_bind(registry, name, &ext_workspace_manager_v1_interface, version));
|
||||||
|
ext_workspace_manager_v1_add_listener(workspace_manager_, &workspace_manager_impl, this);
|
||||||
|
}
|
||||||
|
|
||||||
void Taskbar::register_seat(struct wl_registry* registry, uint32_t name, uint32_t version) {
|
void Taskbar::register_seat(struct wl_registry* registry, uint32_t name, uint32_t version) {
|
||||||
if (seat_) {
|
if (seat_) {
|
||||||
spdlog::warn("Register seat again although already existing!");
|
spdlog::warn("Register seat again although already existing!");
|
||||||
@@ -717,6 +852,64 @@ void Taskbar::handle_finished() {
|
|||||||
manager_ = nullptr;
|
manager_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Taskbar::handle_workspace_group_create(struct ext_workspace_group_handle_v1* handle) {
|
||||||
|
ext_workspace_group_handle_v1_add_listener(handle, &workspace_group_impl, this);
|
||||||
|
workspace_groups_.push_back(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Taskbar::handle_workspace_group_removed(struct ext_workspace_group_handle_v1* handle) {
|
||||||
|
const auto group = std::find(workspace_groups_.begin(), workspace_groups_.end(), handle);
|
||||||
|
if (group != workspace_groups_.end()) {
|
||||||
|
ext_workspace_group_handle_v1_destroy(*group);
|
||||||
|
workspace_groups_.erase(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Taskbar::handle_workspace_create(struct ext_workspace_handle_v1* handle) {
|
||||||
|
auto workspace = std::make_unique<WorkspaceState>(WorkspaceState{this, handle});
|
||||||
|
ext_workspace_handle_v1_add_listener(handle, &workspace_handle_impl, workspace.get());
|
||||||
|
workspaces_.push_back(std::move(workspace));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Taskbar::handle_workspace_done() {
|
||||||
|
const auto active_workspace =
|
||||||
|
std::find_if(workspaces_.begin(), workspaces_.end(), [](const auto& workspace) {
|
||||||
|
return workspace->state & EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE;
|
||||||
|
});
|
||||||
|
current_workspace_ =
|
||||||
|
active_workspace == workspaces_.end() ? nullptr : (*active_workspace)->handle;
|
||||||
|
if (current_workspace_) {
|
||||||
|
const auto active_task =
|
||||||
|
std::find_if(tasks_.begin(), tasks_.end(), [](const auto& task) { return task->active(); });
|
||||||
|
if (active_task != tasks_.end()) {
|
||||||
|
(*active_task)->set_workspace(current_workspace_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update_bar_css_classes();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Taskbar::handle_workspace_finished() { workspace_manager_ = nullptr; }
|
||||||
|
|
||||||
|
void Taskbar::handle_workspace_removed(struct ext_workspace_handle_v1* handle) {
|
||||||
|
if (current_workspace_ == handle) {
|
||||||
|
current_workspace_ = nullptr;
|
||||||
|
}
|
||||||
|
for (auto& task : tasks_) {
|
||||||
|
if (task->workspace() == handle) {
|
||||||
|
task->set_workspace(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto workspace =
|
||||||
|
std::find_if(workspaces_.begin(), workspaces_.end(),
|
||||||
|
[handle](const auto& workspace) { return workspace->handle == handle; });
|
||||||
|
if (workspace != workspaces_.end()) {
|
||||||
|
ext_workspace_handle_v1_destroy((*workspace)->handle);
|
||||||
|
workspaces_.erase(workspace);
|
||||||
|
}
|
||||||
|
update_bar_css_classes();
|
||||||
|
}
|
||||||
|
|
||||||
void Taskbar::add_button(Gtk::Button& bt) {
|
void Taskbar::add_button(Gtk::Button& bt) {
|
||||||
box_.pack_start(bt, false, false);
|
box_.pack_start(bt, false, false);
|
||||||
box_.get_style_context()->remove_class("empty");
|
box_.get_style_context()->remove_class("empty");
|
||||||
@@ -741,6 +934,56 @@ void Taskbar::remove_task(uint32_t id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks_.erase(it);
|
tasks_.erase(it);
|
||||||
|
update_bar_css_classes();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Taskbar::assign_current_workspace(Task& task) {
|
||||||
|
if (current_workspace_) {
|
||||||
|
task.set_workspace(current_workspace_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Taskbar::update_bar_css_classes() {
|
||||||
|
if (!config_["bar-css-states"].asBool()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto active_task = std::find_if(tasks_.begin(), tasks_.end(), [](const TaskPtr& task) {
|
||||||
|
return task->visible() && task->active();
|
||||||
|
});
|
||||||
|
|
||||||
|
const bool has_active_task = active_task != tasks_.end();
|
||||||
|
const auto on_current_workspace = [this](const TaskPtr& task) {
|
||||||
|
if (!current_workspace_) {
|
||||||
|
return task->active();
|
||||||
|
}
|
||||||
|
return task->workspace() == current_workspace_;
|
||||||
|
};
|
||||||
|
const bool has_maximized_task =
|
||||||
|
std::any_of(tasks_.begin(), tasks_.end(), [&on_current_workspace](const TaskPtr& task) {
|
||||||
|
return task->visible() && !task->minimized() && on_current_workspace(task) &&
|
||||||
|
task->maximized();
|
||||||
|
});
|
||||||
|
const bool has_fullscreen_task =
|
||||||
|
std::any_of(tasks_.begin(), tasks_.end(), [&on_current_workspace](const TaskPtr& task) {
|
||||||
|
return task->visible() && !task->minimized() && on_current_workspace(task) &&
|
||||||
|
task->fullscreen();
|
||||||
|
});
|
||||||
|
set_bar_css_class("toplevel-active", has_active_task);
|
||||||
|
set_bar_css_class("toplevel-maximized", has_maximized_task);
|
||||||
|
set_bar_css_class("toplevel-minimized", has_active_task && (*active_task)->minimized());
|
||||||
|
set_bar_css_class("toplevel-fullscreen", has_fullscreen_task);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Taskbar::set_bar_css_class(const std::string& class_name, bool enabled) {
|
||||||
|
const auto style = bar_.window.get_style_context();
|
||||||
|
if (enabled && !style->has_class(class_name)) {
|
||||||
|
spdlog::trace("Adding bar class: {}", class_name);
|
||||||
|
style->add_class(class_name);
|
||||||
|
} else if (!enabled && style->has_class(class_name)) {
|
||||||
|
spdlog::trace("Removing bar class: {}", class_name);
|
||||||
|
style->remove_class(class_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Taskbar::show_output(struct wl_output* output) const {
|
bool Taskbar::show_output(struct wl_output* output) const {
|
||||||
|
|||||||
Reference in New Issue
Block a user