Merge pull request #1419 from RobertMueller2/sway-window-newstyles
commit
e59b4e4c93
|
@ -19,10 +19,11 @@ class Window : public AIconLabel, public sigc::trackable {
|
|||
auto update() -> void;
|
||||
|
||||
private:
|
||||
void setClass(std::string classname, bool enable);
|
||||
void onEvent(const struct Ipc::ipc_response&);
|
||||
void onCmd(const struct Ipc::ipc_response&);
|
||||
std::tuple<std::size_t, int, std::string, std::string, std::string, std::string> getFocusedNode(
|
||||
const Json::Value& nodes, std::string& output);
|
||||
std::tuple<std::size_t, int, int, std::string, std::string, std::string, std::string, std::string>
|
||||
getFocusedNode(const Json::Value& nodes, std::string& output);
|
||||
void getTree();
|
||||
void updateAppIconName();
|
||||
void updateAppIcon();
|
||||
|
@ -32,12 +33,14 @@ class Window : public AIconLabel, public sigc::trackable {
|
|||
int windowId_;
|
||||
std::string app_id_;
|
||||
std::string app_class_;
|
||||
std::string layout_;
|
||||
std::string old_app_id_;
|
||||
std::size_t app_nb_;
|
||||
std::string shell_;
|
||||
unsigned app_icon_size_{24};
|
||||
bool update_app_icon_{true};
|
||||
std::string app_icon_name_;
|
||||
int floating_count_;
|
||||
util::JsonParser parser_;
|
||||
std::mutex mutex_;
|
||||
Ipc ipc_;
|
||||
|
|
|
@ -66,6 +66,25 @@ Addressed by *sway/window*
|
|||
default: true ++
|
||||
Option to disable tooltip on hover.
|
||||
|
||||
*all-outputs*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Option to show the focused window along with its workspace styles on all outputs.
|
||||
|
||||
*offscreen-css*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
Only effective when all-outputs is true. Adds style according to present windows on unfocused outputs instead of showing the focused window and style.
|
||||
|
||||
*offscreen-css-text*: ++
|
||||
typeof: string ++
|
||||
Only effective when both all-outputs and offscreen-style are true. On screens currently not focused, show the given text along with that workspaces styles.
|
||||
|
||||
*show-focused-workspace-name*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
If the workspace itself is focused and the workspace contains nodes or floating_nodes, show the workspace name. If not set, text remains empty but styles according to nodes in the workspace are still applied.
|
||||
|
||||
*rewrite*: ++
|
||||
typeof: object ++
|
||||
Rules to rewrite window title. See *rewrite rules*.
|
||||
|
@ -117,6 +136,10 @@ Invalid expressions (e.g., mismatched parentheses) are skipped.
|
|||
# STYLE
|
||||
|
||||
- *#window*
|
||||
- *window#waybar.empty* When no windows is in the workspace
|
||||
- *window#waybar.solo* When one window is in the workspace
|
||||
- *window#waybar.empty* When no windows are in the workspace, or screen is not focused and offscreen-text option is not set
|
||||
- *window#waybar.solo* When one tiled window is in the workspace
|
||||
- *window#waybar.floating* When there are only floating windows in the workspace
|
||||
- *window#waybar.stacked* When there is more than one window in the workspace and the workspace layout is stacked
|
||||
- *window#waybar.tabbed* When there is more than one window in the workspace and the workspace layout is tabbed
|
||||
- *window#waybar.tiled* When there is more than one window in the workspace and the workspace layout is splith or splitv
|
||||
- *window#waybar.<app_id>* Where *app_id* is the app_id or *instance* name like (*chromium*) of the only window in the workspace
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
namespace waybar::modules::sway {
|
||||
|
||||
Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
||||
: AIconLabel(config, "window", id, "{title}", 0, true), bar_(bar), windowId_(-1) {
|
||||
: AIconLabel(config, "window", id, "{}", 0, true), bar_(bar), windowId_(-1) {
|
||||
// Icon size
|
||||
if (config_["icon-size"].isUInt()) {
|
||||
app_icon_size_ = config["icon-size"].asUInt();
|
||||
|
@ -35,6 +35,7 @@ Window::Window(const std::string& id, const Bar& bar, const Json::Value& config)
|
|||
ipc_.handleEvent();
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Window: {}", e.what());
|
||||
spdlog::trace("Window::Window exception");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -46,12 +47,13 @@ void Window::onCmd(const struct Ipc::ipc_response& res) {
|
|||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto payload = parser_.parse(res.payload);
|
||||
auto output = payload["output"].isString() ? payload["output"].asString() : "";
|
||||
std::tie(app_nb_, windowId_, window_, app_id_, app_class_, shell_) =
|
||||
std::tie(app_nb_, floating_count_, windowId_, window_, app_id_, app_class_, shell_, layout_) =
|
||||
getFocusedNode(payload["nodes"], output);
|
||||
updateAppIconName();
|
||||
dp.emit();
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Window: {}", e.what());
|
||||
spdlog::trace("Window::onCmd exception");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,27 +158,52 @@ void Window::updateAppIcon() {
|
|||
}
|
||||
|
||||
auto Window::update() -> void {
|
||||
if (!old_app_id_.empty()) {
|
||||
bar_.window.get_style_context()->remove_class(old_app_id_);
|
||||
}
|
||||
spdlog::trace("workspace layout {}, tiled count {}, floating count {}", layout_, app_nb_,
|
||||
floating_count_);
|
||||
|
||||
int mode = 0;
|
||||
if (app_nb_ == 0) {
|
||||
bar_.window.get_style_context()->remove_class("solo");
|
||||
if (!bar_.window.get_style_context()->has_class("empty")) {
|
||||
bar_.window.get_style_context()->add_class("empty");
|
||||
if (floating_count_ == 0) {
|
||||
mode += 1;
|
||||
} else {
|
||||
mode += 4;
|
||||
}
|
||||
} else if (app_nb_ == 1) {
|
||||
bar_.window.get_style_context()->remove_class("empty");
|
||||
if (!bar_.window.get_style_context()->has_class("solo")) {
|
||||
bar_.window.get_style_context()->add_class("solo");
|
||||
mode += 2;
|
||||
} else {
|
||||
if (layout_ == "tabbed") {
|
||||
mode += 8;
|
||||
} else if (layout_ == "stacked") {
|
||||
mode += 16;
|
||||
} else {
|
||||
mode += 32;
|
||||
}
|
||||
if (!app_id_.empty() && !bar_.window.get_style_context()->has_class(app_id_)) {
|
||||
bar_.window.get_style_context()->add_class(app_id_);
|
||||
old_app_id_ = app_id_;
|
||||
}
|
||||
} else {
|
||||
bar_.window.get_style_context()->remove_class("solo");
|
||||
bar_.window.get_style_context()->remove_class("empty");
|
||||
}
|
||||
|
||||
if (!old_app_id_.empty() && ((mode & 2) == 0 || old_app_id_ != app_id_) &&
|
||||
bar_.window.get_style_context()->has_class(old_app_id_)) {
|
||||
spdlog::trace("Removing app_id class: {}", old_app_id_);
|
||||
bar_.window.get_style_context()->remove_class(old_app_id_);
|
||||
old_app_id_ = "";
|
||||
}
|
||||
|
||||
setClass("empty", ((mode & 1) > 0));
|
||||
setClass("solo", ((mode & 2) > 0));
|
||||
setClass("floating", ((mode & 4) > 0));
|
||||
setClass("tabbed", ((mode & 8) > 0));
|
||||
setClass("stacked", ((mode & 16) > 0));
|
||||
setClass("tiled", ((mode & 32) > 0));
|
||||
|
||||
if ((mode & 2) > 0 && !app_id_.empty() && !bar_.window.get_style_context()->has_class(app_id_)) {
|
||||
spdlog::trace("Adding app_id class: {}", app_id_);
|
||||
bar_.window.get_style_context()->add_class(app_id_);
|
||||
old_app_id_ = app_id_;
|
||||
}
|
||||
|
||||
label_.set_markup(fmt::format(
|
||||
format_, fmt::arg("title", waybar::util::rewriteTitle(window_, config_["rewrite"])),
|
||||
fmt::arg("app_id", app_id_), fmt::arg("shell", shell_)));
|
||||
|
@ -190,71 +217,143 @@ auto Window::update() -> void {
|
|||
AIconLabel::update();
|
||||
}
|
||||
|
||||
int leafNodesInWorkspace(const Json::Value& node) {
|
||||
void Window::setClass(std::string classname, bool enable) {
|
||||
if (enable) {
|
||||
if (!bar_.window.get_style_context()->has_class(classname)) {
|
||||
bar_.window.get_style_context()->add_class(classname);
|
||||
}
|
||||
} else {
|
||||
bar_.window.get_style_context()->remove_class(classname);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<int, int> leafNodesInWorkspace(const Json::Value& node) {
|
||||
auto const& nodes = node["nodes"];
|
||||
auto const& floating_nodes = node["floating_nodes"];
|
||||
if (nodes.empty() && floating_nodes.empty()) {
|
||||
if (node["type"] == "workspace")
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
if (node["type"].asString() == "workspace")
|
||||
return {0, 0};
|
||||
else if (node["type"].asString() == "floating_con") {
|
||||
return {0, 1};
|
||||
} else {
|
||||
return {1, 0};
|
||||
}
|
||||
}
|
||||
int sum = 0;
|
||||
if (!nodes.empty()) {
|
||||
for (auto const& node : nodes) sum += leafNodesInWorkspace(node);
|
||||
}
|
||||
if (!floating_nodes.empty()) {
|
||||
for (auto const& node : floating_nodes) sum += leafNodesInWorkspace(node);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
std::tuple<std::size_t, int, std::string, std::string, std::string, std::string> gfnWithWorkspace(
|
||||
const Json::Value& nodes, std::string& output, const Json::Value& config_, const Bar& bar_,
|
||||
Json::Value& parentWorkspace) {
|
||||
int floating_sum = 0;
|
||||
for (auto const& node : nodes) {
|
||||
if (node["output"].isString()) {
|
||||
output = node["output"].asString();
|
||||
}
|
||||
// found node
|
||||
if (node["focused"].asBool() && (node["type"] == "con" || node["type"] == "floating_con")) {
|
||||
if ((!config_["all-outputs"].asBool() && output == bar_.output->name) ||
|
||||
config_["all-outputs"].asBool()) {
|
||||
auto app_id = node["app_id"].isString() ? node["app_id"].asString()
|
||||
: node["window_properties"]["instance"].asString();
|
||||
const auto app_class = node["window_properties"]["class"].isString()
|
||||
? node["window_properties"]["class"].asString()
|
||||
: "";
|
||||
|
||||
const auto shell = node["shell"].isString() ? node["shell"].asString() : "";
|
||||
|
||||
int nb = node.size();
|
||||
if (parentWorkspace != 0) nb = leafNodesInWorkspace(parentWorkspace);
|
||||
return {nb, node["id"].asInt(), Glib::Markup::escape_text(node["name"].asString()),
|
||||
app_id, app_class, shell};
|
||||
}
|
||||
}
|
||||
// iterate
|
||||
if (node["type"] == "workspace") parentWorkspace = node;
|
||||
auto [nb, id, name, app_id, app_class, shell] =
|
||||
gfnWithWorkspace(node["nodes"], output, config_, bar_, parentWorkspace);
|
||||
if (id > -1 && !name.empty()) {
|
||||
return {nb, id, name, app_id, app_class, shell};
|
||||
}
|
||||
// Search for floating node
|
||||
std::tie(nb, id, name, app_id, app_class, shell) =
|
||||
gfnWithWorkspace(node["floating_nodes"], output, config_, bar_, parentWorkspace);
|
||||
if (id > -1 && !name.empty()) {
|
||||
return {nb, id, name, app_id, app_class, shell};
|
||||
}
|
||||
std::pair all_leaf_nodes = leafNodesInWorkspace(node);
|
||||
sum += all_leaf_nodes.first;
|
||||
floating_sum += all_leaf_nodes.second;
|
||||
}
|
||||
return {0, -1, "", "", "", ""};
|
||||
for (auto const& node : floating_nodes) {
|
||||
std::pair all_leaf_nodes = leafNodesInWorkspace(node);
|
||||
sum += all_leaf_nodes.first;
|
||||
floating_sum += all_leaf_nodes.second;
|
||||
}
|
||||
return {sum, floating_sum};
|
||||
}
|
||||
|
||||
std::tuple<std::size_t, int, std::string, std::string, std::string, std::string>
|
||||
std::tuple<std::size_t, int, int, std::string, std::string, std::string, std::string, std::string>
|
||||
gfnWithWorkspace(const Json::Value& nodes, std::string& output, const Json::Value& config_,
|
||||
const Bar& bar_, Json::Value& parentWorkspace,
|
||||
const Json::Value& immediateParent) {
|
||||
for (auto const& node : nodes) {
|
||||
if (node["type"].asString() == "output") {
|
||||
if ((!config_["all-outputs"].asBool() || config_["offscreen-css"].asBool()) &&
|
||||
(node["name"].asString() != bar_.output->name)) {
|
||||
continue;
|
||||
}
|
||||
output = node["name"].asString();
|
||||
} else if (node["type"].asString() == "workspace") {
|
||||
// needs to be a string comparison, because filterWorkspace is the current_workspace
|
||||
if (node["name"].asString() != immediateParent["current_workspace"].asString()) {
|
||||
continue;
|
||||
}
|
||||
if (node["focused"].asBool()) {
|
||||
std::pair all_leaf_nodes = leafNodesInWorkspace(node);
|
||||
return {all_leaf_nodes.first,
|
||||
all_leaf_nodes.second,
|
||||
node["id"].asInt(),
|
||||
(((all_leaf_nodes.first > 0) || (all_leaf_nodes.second > 0)) &&
|
||||
(config_["show-focused-workspace-name"].asBool()))
|
||||
? node["name"].asString()
|
||||
: "",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
node["layout"].asString()};
|
||||
}
|
||||
parentWorkspace = node;
|
||||
} else if ((node["type"].asString() == "con" || node["type"].asString() == "floating_con") &&
|
||||
(node["focused"].asBool())) {
|
||||
// found node
|
||||
spdlog::trace("actual output {}, output found {}, node (focused) found {}", bar_.output->name,
|
||||
output, node["name"].asString());
|
||||
auto app_id = node["app_id"].isString() ? node["app_id"].asString()
|
||||
: node["window_properties"]["instance"].asString();
|
||||
const auto app_class = node["window_properties"]["class"].isString()
|
||||
? node["window_properties"]["class"].asString()
|
||||
: "";
|
||||
const auto shell = node["shell"].isString() ? node["shell"].asString() : "";
|
||||
int nb = node.size();
|
||||
int floating_count = 0;
|
||||
std::string workspace_layout = "";
|
||||
if (!parentWorkspace.isNull()) {
|
||||
std::pair all_leaf_nodes = leafNodesInWorkspace(parentWorkspace);
|
||||
nb = all_leaf_nodes.first;
|
||||
floating_count = all_leaf_nodes.second;
|
||||
workspace_layout = parentWorkspace["layout"].asString();
|
||||
}
|
||||
return {nb,
|
||||
floating_count,
|
||||
node["id"].asInt(),
|
||||
Glib::Markup::escape_text(node["name"].asString()),
|
||||
app_id,
|
||||
app_class,
|
||||
shell,
|
||||
workspace_layout};
|
||||
}
|
||||
|
||||
// iterate
|
||||
auto [nb, f, id, name, app_id, app_class, shell, workspace_layout] =
|
||||
gfnWithWorkspace(node["nodes"], output, config_, bar_, parentWorkspace, node);
|
||||
auto [nb2, f2, id2, name2, app_id2, app_class2, shell2, workspace_layout2] =
|
||||
gfnWithWorkspace(node["floating_nodes"], output, config_, bar_, parentWorkspace, node);
|
||||
|
||||
// if ((id > 0 || ((id2 < 0 || name2.empty()) && id > -1)) && !name.empty()) {
|
||||
if ((id > 0) || (id2 < 0 && id > -1)) {
|
||||
return {nb, f, id, name, app_id, app_class, shell, workspace_layout};
|
||||
} else if (id2 > 0 && !name2.empty()) {
|
||||
return {nb2, f2, id2, name2, app_id2, app_class, shell2, workspace_layout2};
|
||||
}
|
||||
}
|
||||
|
||||
// this only comes into effect when no focused children are present
|
||||
if (config_["all-outputs"].asBool() && config_["offscreen-css"].asBool() &&
|
||||
immediateParent["type"].asString() == "workspace") {
|
||||
std::pair all_leaf_nodes = leafNodesInWorkspace(immediateParent);
|
||||
// using an empty string as default ensures that no window depending styles are set due to the
|
||||
// checks above for !name.empty()
|
||||
return {all_leaf_nodes.first,
|
||||
all_leaf_nodes.second,
|
||||
0,
|
||||
(all_leaf_nodes.first > 0 || all_leaf_nodes.second > 0)
|
||||
? config_["offscreen-css-text"].asString()
|
||||
: "",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
immediateParent["layout"].asString()};
|
||||
}
|
||||
|
||||
return {0, 0, -1, "", "", "", "", ""};
|
||||
}
|
||||
|
||||
std::tuple<std::size_t, int, int, std::string, std::string, std::string, std::string, std::string>
|
||||
Window::getFocusedNode(const Json::Value& nodes, std::string& output) {
|
||||
Json::Value placeholder = 0;
|
||||
return gfnWithWorkspace(nodes, output, config_, bar_, placeholder);
|
||||
Json::Value placeholder = Json::Value::null;
|
||||
return gfnWithWorkspace(nodes, output, config_, bar_, placeholder, placeholder);
|
||||
}
|
||||
|
||||
void Window::getTree() {
|
||||
|
@ -262,6 +361,7 @@ void Window::getTree() {
|
|||
ipc_.sendCmd(IPC_GET_TREE);
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("Window: {}", e.what());
|
||||
spdlog::trace("Window::getTree exception");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue