Initial implementation of workspace taskbars
Add a list of window titles and icons to each workspace (like wlr/taskbar but grouped by workspace). Only implemented on hyprland for now.
This commit is contained in:
@ -26,10 +26,19 @@ namespace waybar::modules::hyprland {
|
|||||||
|
|
||||||
class Workspaces;
|
class Workspaces;
|
||||||
|
|
||||||
|
struct WindowRepr {
|
||||||
|
std::string window_class;
|
||||||
|
std::string window_title;
|
||||||
|
std::string repr_rewrite;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool empty() const { return repr_rewrite.empty(); }
|
||||||
|
};
|
||||||
|
|
||||||
class WindowCreationPayload {
|
class WindowCreationPayload {
|
||||||
public:
|
public:
|
||||||
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
|
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
|
||||||
std::string window_repr);
|
WindowRepr window_repr);
|
||||||
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
|
WindowCreationPayload(std::string workspace_name, WindowAddress window_address,
|
||||||
std::string window_class, std::string window_title);
|
std::string window_class, std::string window_title);
|
||||||
WindowCreationPayload(Json::Value const& client_data);
|
WindowCreationPayload(Json::Value const& client_data);
|
||||||
@ -37,7 +46,7 @@ class WindowCreationPayload {
|
|||||||
int incrementTimeSpentUncreated();
|
int incrementTimeSpentUncreated();
|
||||||
bool isEmpty(Workspaces& workspace_manager);
|
bool isEmpty(Workspaces& workspace_manager);
|
||||||
bool reprIsReady() const { return std::holds_alternative<Repr>(m_window); }
|
bool reprIsReady() const { return std::holds_alternative<Repr>(m_window); }
|
||||||
std::string repr(Workspaces& workspace_manager);
|
WindowRepr repr(Workspaces& workspace_manager);
|
||||||
|
|
||||||
std::string getWorkspaceName() const { return m_workspaceName; }
|
std::string getWorkspaceName() const { return m_workspaceName; }
|
||||||
WindowAddress getAddress() const { return m_windowAddress; }
|
WindowAddress getAddress() const { return m_windowAddress; }
|
||||||
@ -48,7 +57,7 @@ class WindowCreationPayload {
|
|||||||
void clearAddr();
|
void clearAddr();
|
||||||
void clearWorkspaceName();
|
void clearWorkspaceName();
|
||||||
|
|
||||||
using Repr = std::string;
|
using Repr = WindowRepr;
|
||||||
using ClassAndTitle = std::pair<std::string, std::string>;
|
using ClassAndTitle = std::pair<std::string, std::string>;
|
||||||
std::variant<Repr, ClassAndTitle> m_window;
|
std::variant<Repr, ClassAndTitle> m_window;
|
||||||
|
|
||||||
|
@ -56,11 +56,11 @@ class Workspace {
|
|||||||
void setOutput(std::string const& value) { m_output = value; };
|
void setOutput(std::string const& value) { m_output = value; };
|
||||||
bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); }
|
bool containsWindow(WindowAddress const& addr) const { return m_windowMap.contains(addr); }
|
||||||
void insertWindow(WindowCreationPayload create_window_paylod);
|
void insertWindow(WindowCreationPayload create_window_paylod);
|
||||||
std::string removeWindow(WindowAddress const& addr);
|
WindowRepr removeWindow(WindowAddress const& addr);
|
||||||
void initializeWindowMap(const Json::Value& clients_data);
|
void initializeWindowMap(const Json::Value& clients_data);
|
||||||
|
|
||||||
bool onWindowOpened(WindowCreationPayload const& create_window_paylod);
|
bool onWindowOpened(WindowCreationPayload const& create_window_paylod);
|
||||||
std::optional<std::string> closeWindow(WindowAddress const& addr);
|
std::optional<WindowRepr> closeWindow(WindowAddress const& addr);
|
||||||
|
|
||||||
void update(const std::string& format, const std::string& icon);
|
void update(const std::string& format, const std::string& icon);
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ class Workspace {
|
|||||||
bool m_isUrgent = false;
|
bool m_isUrgent = false;
|
||||||
bool m_isVisible = false;
|
bool m_isVisible = false;
|
||||||
|
|
||||||
std::map<WindowAddress, std::string> m_windowMap;
|
std::map<WindowAddress, WindowRepr, std::less<>> m_windowMap;
|
||||||
|
|
||||||
Gtk::Button m_button;
|
Gtk::Button m_button;
|
||||||
Gtk::Box m_content;
|
Gtk::Box m_content;
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "modules/hyprland/windowcreationpayload.hpp"
|
#include "modules/hyprland/windowcreationpayload.hpp"
|
||||||
#include "modules/hyprland/workspace.hpp"
|
#include "modules/hyprland/workspace.hpp"
|
||||||
#include "util/enum.hpp"
|
#include "util/enum.hpp"
|
||||||
|
#include "util/icon_loader.hpp"
|
||||||
#include "util/regex_collection.hpp"
|
#include "util/regex_collection.hpp"
|
||||||
|
|
||||||
using WindowAddress = std::string;
|
using WindowAddress = std::string;
|
||||||
@ -45,6 +46,7 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
bool isWorkspaceIgnored(std::string const& workspace_name);
|
bool isWorkspaceIgnored(std::string const& workspace_name);
|
||||||
|
|
||||||
bool windowRewriteConfigUsesTitle() const { return m_anyWindowRewriteRuleUsesTitle; }
|
bool windowRewriteConfigUsesTitle() const { return m_anyWindowRewriteRuleUsesTitle; }
|
||||||
|
const IconLoader& iconLoader() const { return m_iconLoader; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onEvent(const std::string& e) override;
|
void onEvent(const std::string& e) override;
|
||||||
@ -119,7 +121,7 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
// Map for windows stored in workspaces not present in the current bar.
|
// Map for windows stored in workspaces not present in the current bar.
|
||||||
// This happens when the user has multiple monitors (hence, multiple bars)
|
// This happens when the user has multiple monitors (hence, multiple bars)
|
||||||
// and doesn't share windows accross bars (a.k.a `all-outputs` = false)
|
// and doesn't share windows accross bars (a.k.a `all-outputs` = false)
|
||||||
std::map<WindowAddress, std::string> m_orphanWindowMap;
|
std::map<WindowAddress, WindowRepr> m_orphanWindowMap;
|
||||||
|
|
||||||
enum class SortMethod { ID, NAME, NUMBER, DEFAULT };
|
enum class SortMethod { ID, NAME, NUMBER, DEFAULT };
|
||||||
util::EnumParser<SortMethod> m_enumParser;
|
util::EnumParser<SortMethod> m_enumParser;
|
||||||
@ -136,6 +138,7 @@ class Workspaces : public AModule, public EventHandler {
|
|||||||
bool m_anyWindowRewriteRuleUsesTitle = false;
|
bool m_anyWindowRewriteRuleUsesTitle = false;
|
||||||
std::string m_formatWindowSeparator;
|
std::string m_formatWindowSeparator;
|
||||||
|
|
||||||
|
IconLoader m_iconLoader;
|
||||||
bool m_withIcon;
|
bool m_withIcon;
|
||||||
uint64_t m_monitorId;
|
uint64_t m_monitorId;
|
||||||
std::string m_activeWorkspaceName;
|
std::string m_activeWorkspaceName;
|
||||||
|
@ -20,7 +20,7 @@ WindowCreationPayload::WindowCreationPayload(Json::Value const &client_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
WindowCreationPayload::WindowCreationPayload(std::string workspace_name,
|
WindowCreationPayload::WindowCreationPayload(std::string workspace_name,
|
||||||
WindowAddress window_address, std::string window_repr)
|
WindowAddress window_address, WindowRepr window_repr)
|
||||||
: m_window(std::move(window_repr)),
|
: m_window(std::move(window_repr)),
|
||||||
m_windowAddress(std::move(window_address)),
|
m_windowAddress(std::move(window_address)),
|
||||||
m_workspaceName(std::move(workspace_name)) {
|
m_workspaceName(std::move(workspace_name)) {
|
||||||
@ -92,13 +92,13 @@ void WindowCreationPayload::moveToWorksace(std::string &new_workspace_name) {
|
|||||||
m_workspaceName = new_workspace_name;
|
m_workspaceName = new_workspace_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string WindowCreationPayload::repr(Workspaces &workspace_manager) {
|
WindowRepr WindowCreationPayload::repr(Workspaces &workspace_manager) {
|
||||||
if (std::holds_alternative<Repr>(m_window)) {
|
if (std::holds_alternative<Repr>(m_window)) {
|
||||||
return std::get<Repr>(m_window);
|
return std::get<Repr>(m_window);
|
||||||
}
|
}
|
||||||
if (std::holds_alternative<ClassAndTitle>(m_window)) {
|
if (std::holds_alternative<ClassAndTitle>(m_window)) {
|
||||||
auto [window_class, window_title] = std::get<ClassAndTitle>(m_window);
|
auto [window_class, window_title] = std::get<ClassAndTitle>(m_window);
|
||||||
return workspace_manager.getRewrite(window_class, window_title);
|
return {window_class, window_title, workspace_manager.getRewrite(window_class, window_title)};
|
||||||
}
|
}
|
||||||
// Unreachable
|
// Unreachable
|
||||||
spdlog::error("WorkspaceWindow::repr: Unreachable");
|
spdlog::error("WorkspaceWindow::repr: Unreachable");
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "modules/hyprland/workspaces.hpp"
|
#include "modules/hyprland/workspaces.hpp"
|
||||||
|
#include "util/icon_loader.hpp"
|
||||||
|
|
||||||
namespace waybar::modules::hyprland {
|
namespace waybar::modules::hyprland {
|
||||||
|
|
||||||
@ -31,7 +32,13 @@ Workspace::Workspace(const Json::Value &workspace_data, Workspaces &workspace_ma
|
|||||||
false);
|
false);
|
||||||
|
|
||||||
m_button.set_relief(Gtk::RELIEF_NONE);
|
m_button.set_relief(Gtk::RELIEF_NONE);
|
||||||
|
if (true) {
|
||||||
|
// TODO-WorkspaceTaskbar: Allow vertical?
|
||||||
|
m_content.set_orientation(Gtk::ORIENTATION_HORIZONTAL);
|
||||||
|
m_content.pack_start(m_label, false, false);
|
||||||
|
} else {
|
||||||
m_content.set_center_widget(m_label);
|
m_content.set_center_widget(m_label);
|
||||||
|
}
|
||||||
m_button.add(m_content);
|
m_button.add(m_content);
|
||||||
|
|
||||||
initializeWindowMap(clients_data);
|
initializeWindowMap(clients_data);
|
||||||
@ -46,7 +53,7 @@ void addOrRemoveClass(const Glib::RefPtr<Gtk::StyleContext> &context, bool condi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> Workspace::closeWindow(WindowAddress const &addr) {
|
std::optional<WindowRepr> Workspace::closeWindow(WindowAddress const &addr) {
|
||||||
if (m_windowMap.contains(addr)) {
|
if (m_windowMap.contains(addr)) {
|
||||||
return removeWindow(addr);
|
return removeWindow(addr);
|
||||||
}
|
}
|
||||||
@ -108,8 +115,8 @@ bool Workspace::onWindowOpened(WindowCreationPayload const &create_window_paylod
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Workspace::removeWindow(WindowAddress const &addr) {
|
WindowRepr Workspace::removeWindow(WindowAddress const &addr) {
|
||||||
std::string windowRepr = m_windowMap[addr];
|
WindowRepr windowRepr = m_windowMap[addr];
|
||||||
m_windowMap.erase(addr);
|
m_windowMap.erase(addr);
|
||||||
return windowRepr;
|
return windowRepr;
|
||||||
}
|
}
|
||||||
@ -199,6 +206,8 @@ void Workspace::update(const std::string &format, const std::string &icon) {
|
|||||||
addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor");
|
addOrRemoveClass(styleContext, m_workspaceManager.getBarOutput() == output(), "hosting-monitor");
|
||||||
|
|
||||||
std::string windows;
|
std::string windows;
|
||||||
|
// TODO-WorkspaceTaskbar
|
||||||
|
if (false) {
|
||||||
auto windowSeparator = m_workspaceManager.getWindowSeparator();
|
auto windowSeparator = m_workspaceManager.getWindowSeparator();
|
||||||
|
|
||||||
bool isNotFirst = false;
|
bool isNotFirst = false;
|
||||||
@ -208,12 +217,42 @@ void Workspace::update(const std::string &format, const std::string &icon) {
|
|||||||
windows.append(windowSeparator);
|
windows.append(windowSeparator);
|
||||||
}
|
}
|
||||||
isNotFirst = true;
|
isNotFirst = true;
|
||||||
windows.append(window_repr);
|
windows.append(window_repr.repr_rewrite);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_label.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()),
|
m_label.set_markup(fmt::format(fmt::runtime(format), fmt::arg("id", id()),
|
||||||
fmt::arg("name", name()), fmt::arg("icon", icon),
|
fmt::arg("name", name()), fmt::arg("icon", icon),
|
||||||
fmt::arg("windows", windows)));
|
fmt::arg("windows", windows)));
|
||||||
|
|
||||||
|
auto children = m_content.get_children();
|
||||||
|
for (auto child : children) {
|
||||||
|
if (child != &m_label) {
|
||||||
|
m_content.remove(*child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &[_addr, window_repr] : m_windowMap) {
|
||||||
|
auto window_box = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL);
|
||||||
|
auto window_icon = Gtk::make_managed<Gtk::Image>();
|
||||||
|
auto window_label = Gtk::make_managed<Gtk::Label>(window_repr.window_title);
|
||||||
|
|
||||||
|
// TODO-WorkspaceTaskbar: customizable max width and ellipsize
|
||||||
|
window_label->set_max_width_chars(20);
|
||||||
|
window_label->set_ellipsize(Pango::ELLIPSIZE_END);
|
||||||
|
|
||||||
|
// TODO-WorkspaceTaskbar: support themes
|
||||||
|
auto app_info_ = IconLoader::get_app_info_from_app_id_list(window_repr.window_class);
|
||||||
|
// TODO-WorkspaceTaskbar: icon size
|
||||||
|
m_workspaceManager.iconLoader().image_load_icon(*window_icon, app_info_, 24);
|
||||||
|
|
||||||
|
window_box->pack_start(*window_icon, false, false);
|
||||||
|
window_box->pack_start(*window_label, true, true);
|
||||||
|
window_box->set_tooltip_text(window_repr.window_title);
|
||||||
|
|
||||||
|
m_content.pack_start(*window_box, true, false);
|
||||||
|
window_box->show_all();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace waybar::modules::hyprland
|
} // namespace waybar::modules::hyprland
|
||||||
|
@ -481,7 +481,7 @@ void Workspaces::onWindowMoved(std::string const &payload) {
|
|||||||
|
|
||||||
std::string workspaceName = payload.substr(nextCommaIdx + 1, payload.length() - nextCommaIdx);
|
std::string workspaceName = payload.substr(nextCommaIdx + 1, payload.length() - nextCommaIdx);
|
||||||
|
|
||||||
std::string windowRepr;
|
WindowRepr windowRepr;
|
||||||
|
|
||||||
// If the window was still queued to be created, just change its destination
|
// If the window was still queued to be created, just change its destination
|
||||||
// and exit
|
// and exit
|
||||||
|
Reference in New Issue
Block a user