Merge pull request #1221 from Anakael/pr/anakael/add-name-to-taskbar

[Taskbar] feat: Add name format replacementf feat: Add app ids mapping (ready for review)
This commit is contained in:
Alex 2021-12-23 23:01:21 +01:00 committed by GitHub
commit 8ec321ddaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 140 additions and 75 deletions

View File

@ -3,11 +3,13 @@
#include "AModule.hpp" #include "AModule.hpp"
#include "bar.hpp" #include "bar.hpp"
#include "client.hpp" #include "client.hpp"
#include "giomm/desktopappinfo.h"
#include "util/json.hpp" #include "util/json.hpp"
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
#include <unordered_set> #include <unordered_set>
#include <gdk/gdk.h> #include <gdk/gdk.h>
@ -61,15 +63,18 @@ class Task
Gtk::Image icon_; Gtk::Image icon_;
Gtk::Label text_before_; Gtk::Label text_before_;
Gtk::Label text_after_; Gtk::Label text_after_;
bool button_visible_ = false; Glib::RefPtr<Gio::DesktopAppInfo> app_info_;
bool ignored_ = false; bool button_visible_;
bool ignored_;
bool with_icon_; bool with_icon_ = false;
bool with_name_ = false;
std::string format_before_; std::string format_before_;
std::string format_after_; std::string format_after_;
std::string format_tooltip_; std::string format_tooltip_;
std::string name_;
std::string title_; std::string title_;
std::string app_id_; std::string app_id_;
uint32_t state_ = 0; uint32_t state_ = 0;
@ -77,6 +82,8 @@ class Task
private: private:
std::string repr() const; std::string repr() const;
std::string state_string(bool = false) const; std::string state_string(bool = false) const;
void set_app_info_from_app_id_list(const std::string& app_id_list);
bool image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::IconTheme>& icon_theme, Glib::RefPtr<Gio::DesktopAppInfo> app_info, int size);
void hide_if_ignored(); void hide_if_ignored();
public: public:
@ -136,6 +143,7 @@ class Taskbar : public waybar::AModule
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes_; std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes_;
std::unordered_set<std::string> ignore_list_; std::unordered_set<std::string> ignore_list_;
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 wl_seat *seat_; struct wl_seat *seat_;
@ -158,8 +166,9 @@ class Taskbar : public waybar::AModule
bool show_output(struct wl_output *) const; bool show_output(struct wl_output *) const;
bool all_outputs() const; bool all_outputs() const;
std::vector<Glib::RefPtr<Gtk::IconTheme>> icon_themes() const; const std::vector<Glib::RefPtr<Gtk::IconTheme>>& icon_themes() 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;
}; };
} /* namespace waybar::modules::wlr */ } /* namespace waybar::modules::wlr */

View File

@ -72,10 +72,16 @@ Addressed by *wlr/taskbar*
typeof: array ++ typeof: array ++
List of app_id/titles to be invisible. List of app_id/titles to be invisible.
*app_ids-mapping*: ++
typeof: object ++
Dictionary of app_id to be replaced with
# FORMAT REPLACEMENTS # FORMAT REPLACEMENTS
*{icon}*: The icon of the application. *{icon}*: The icon of the application.
*{title}*: The application name as in desktop file if appropriate desktop fils found, otherwise same as {app_id}
*{title}*: The title of the application. *{title}*: The title of the application.
*{app_id}*: The app_id (== application name) of the application. *{app_id}*: The app_id (== application name) of the application.
@ -105,7 +111,10 @@ Addressed by *wlr/taskbar*
"on-click-middle": "close", "on-click-middle": "close",
"ignore-list": [ "ignore-list": [
"Alacritty" "Alacritty"
] ],
"app_ids-mapping": {
"firefoxdeveloperedition": "firefox-developer-edition"
}
} }
``` ```

View File

@ -11,7 +11,9 @@
#include <cstring> #include <cstring>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include <utility>
#include <fmt/core.h>
#include <gdkmm/monitor.h> #include <gdkmm/monitor.h>
#include <gtkmm/icontheme.h> #include <gtkmm/icontheme.h>
@ -86,8 +88,7 @@ static Glib::RefPtr<Gdk::Pixbuf> load_icon_from_file(std::string icon_path, int
} }
} }
/* Method 1 - get the correct icon name from the desktop file */ static Glib::RefPtr<Gio::DesktopAppInfo> get_app_info_by_name(const std::string& app_id)
static std::string get_from_desktop_app_info(const std::string &app_id)
{ {
static std::vector<std::string> prefixes = search_prefix(); static std::vector<std::string> prefixes = search_prefix();
@ -103,33 +104,29 @@ static std::string get_from_desktop_app_info(const std::string &app_id)
".desktop" ".desktop"
}; };
Glib::RefPtr<Gio::DesktopAppInfo> app_info; for (auto& prefix : prefixes) {
for (auto& folder : app_folders) {
for (auto& suffix : suffixes) {
auto app_info_ = Gio::DesktopAppInfo::create_from_filename(prefix + folder + app_id + suffix);
if (!app_info_) {
continue;
}
for (auto& prefix : prefixes) return app_info_;
for (auto& folder : app_folders) }
for (auto& suffix : suffixes) }
if (!app_info) }
app_info = Gio::DesktopAppInfo::create_from_filename(prefix + folder + app_id + suffix);
if (app_info && app_info->get_icon()) return {};
return app_info->get_icon()->to_string();
return "";
} }
/* Method 2 - use the app_id and check whether there is an icon with this name in the icon theme */ Glib::RefPtr<Gio::DesktopAppInfo> get_desktop_app_info(const std::string &app_id)
static std::string get_from_icon_theme(const Glib::RefPtr<Gtk::IconTheme>& icon_theme,
const std::string &app_id)
{ {
if (icon_theme->lookup_icon(app_id, 24)) auto app_info = get_app_info_by_name(app_id);
return app_id; if (app_info) {
return app_info;
}
return "";
}
/* Method 3 - as last resort perform a search for most appropriate desktop info file */
static std::string get_from_desktop_app_info_search(const std::string &app_id)
{
std::string desktop_file = ""; std::string desktop_file = "";
gchar*** desktop_list = g_desktop_app_info_search(app_id.c_str()); gchar*** desktop_list = g_desktop_app_info_search(app_id.c_str());
@ -151,65 +148,84 @@ static std::string get_from_desktop_app_info_search(const std::string &app_id)
} }
g_free(desktop_list); g_free(desktop_list);
return get_from_desktop_app_info(desktop_file); return get_app_info_by_name(desktop_file);
} }
static bool image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::IconTheme>& icon_theme, void Task::set_app_info_from_app_id_list(const std::string& app_id_list) {
const std::string &app_id_list, int size)
{
std::string app_id; std::string app_id;
std::istringstream stream(app_id_list); std::istringstream stream(app_id_list);
bool found = false;
/* Wayfire sends a list of app-id's in space separated format, other compositors /* Wayfire sends a list of app-id's in space separated format, other compositors
* send a single app-id, but in any case this works fine */ * send a single app-id, but in any case this works fine */
while (stream >> app_id) while (stream >> app_id)
{ {
size_t start = 0, end = app_id.size(); app_info_ = get_desktop_app_info(app_id);
start = app_id.rfind(".", end); if (app_info_) {
std::string app_name = app_id.substr(start+1, app_id.size()); return;
}
auto lower_app_id = app_id; auto lower_app_id = app_id;
std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(), std::transform(lower_app_id.begin(), lower_app_id.end(), lower_app_id.begin(),
[](char c){ return std::tolower(c); }); [](char c){ return std::tolower(c); });
app_info_ = get_desktop_app_info(lower_app_id);
if (app_info_) {
return;
}
std::string icon_name = get_from_icon_theme(icon_theme, app_id); size_t start = 0, end = app_id.size();
start = app_id.rfind(".", end);
std::string app_name = app_id.substr(start+1, app_id.size());
app_info_ = get_desktop_app_info(app_name);
if (app_info_) {
return;
}
if (icon_name.empty()) start = app_id.find("-");
icon_name = get_from_icon_theme(icon_theme, lower_app_id); app_name = app_id.substr(0, start);
if (icon_name.empty()) app_info_ = get_desktop_app_info(app_name);
icon_name = get_from_icon_theme(icon_theme, app_name); }
if (icon_name.empty()) }
icon_name = get_from_desktop_app_info(app_id);
if (icon_name.empty())
icon_name = get_from_desktop_app_info(lower_app_id);
if (icon_name.empty())
icon_name = get_from_desktop_app_info(app_name);
if (icon_name.empty())
icon_name = get_from_desktop_app_info_search(app_id);
if (icon_name.empty()) static std::string get_icon_name_from_icon_theme(const Glib::RefPtr<Gtk::IconTheme>& icon_theme,
icon_name = "unknown"; const std::string &app_id)
{
if (icon_theme->lookup_icon(app_id, 24))
return app_id;
return "";
}
bool Task::image_load_icon(Gtk::Image& image, const Glib::RefPtr<Gtk::IconTheme>& icon_theme, Glib::RefPtr<Gio::DesktopAppInfo> app_info, int size)
{
std::string ret_icon_name = "unknown";
if (app_info) {
std::string icon_name = get_icon_name_from_icon_theme(icon_theme, app_info->get_startup_wm_class());
if (!icon_name.empty()) {
ret_icon_name = icon_name;
} else {
if (app_info->get_icon()) {
ret_icon_name = app_info->get_icon()->to_string();
}
}
}
Glib::RefPtr<Gdk::Pixbuf> pixbuf; Glib::RefPtr<Gdk::Pixbuf> pixbuf;
try { try {
pixbuf = icon_theme->load_icon(icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE); pixbuf = icon_theme->load_icon(ret_icon_name, size, Gtk::ICON_LOOKUP_FORCE_SIZE);
} catch(...) { } catch(...) {
if (Glib::file_test(icon_name, Glib::FILE_TEST_EXISTS)) if (Glib::file_test(ret_icon_name, Glib::FILE_TEST_EXISTS))
pixbuf = load_icon_from_file(icon_name, size); pixbuf = load_icon_from_file(ret_icon_name, size);
else else
pixbuf = {}; pixbuf = {};
} }
if (pixbuf) { if (pixbuf) {
image.set(pixbuf); image.set(pixbuf);
found = true; return true;
break;
}
} }
return found; return false;
} }
/* Task class implementation */ /* Task class implementation */
@ -289,13 +305,15 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar,
content_.show(); content_.show();
button_.add(content_); button_.add(content_);
with_icon_ = false;
format_before_.clear(); format_before_.clear();
format_after_.clear(); format_after_.clear();
if (config_["format"].isString()) { if (config_["format"].isString()) {
/* The user defined a format string, use it */ /* The user defined a format string, use it */
auto format = config_["format"].asString(); auto format = config_["format"].asString();
if (format.find("{name}") != std::string::npos) {
with_name_ = true;
}
auto icon_pos = format.find("{icon}"); auto icon_pos = format.find("{icon}");
if (icon_pos == 0) { if (icon_pos == 0) {
@ -402,13 +420,28 @@ void Task::handle_app_id(const char *app_id)
app_id_ = app_id; app_id_ = app_id;
hide_if_ignored(); hide_if_ignored();
if (!with_icon_) auto ids_replace_map = tbar_->app_ids_replace_map();
if (ids_replace_map.count(app_id_)) {
auto replaced_id = ids_replace_map[app_id_];
spdlog::debug(fmt::format("Task ({}) [{}] app_id was replaced with {}", id_, app_id_, replaced_id));
app_id_ = replaced_id;
}
if (!with_icon_ && !with_name_) {
return; return;
}
set_app_info_from_app_id_list(app_id_);
name_ = app_info_ ? app_info_->get_display_name() : app_id;
if (!with_icon_) {
return;
}
int icon_size = config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16; int icon_size = config_["icon-size"].isInt() ? config_["icon-size"].asInt() : 16;
bool found = false; bool found = false;
for (auto& icon_theme : tbar_->icon_themes()) { for (auto& icon_theme : tbar_->icon_themes()) {
if (image_load_icon(icon_, icon_theme, app_id_, icon_size)) { if (image_load_icon(icon_, icon_theme, app_info_, icon_size)) {
found = true; found = true;
break; break;
} }
@ -564,14 +597,17 @@ void Task::update()
{ {
bool markup = config_["markup"].isBool() ? config_["markup"].asBool() : false; bool markup = config_["markup"].isBool() ? config_["markup"].asBool() : false;
std::string title = title_; std::string title = title_;
std::string name = name_;
std::string app_id = app_id_; std::string app_id = app_id_;
if (markup) { if (markup) {
title = Glib::Markup::escape_text(title); title = Glib::Markup::escape_text(title);
name = Glib::Markup::escape_text(name);
app_id = Glib::Markup::escape_text(app_id); app_id = Glib::Markup::escape_text(app_id);
} }
if (!format_before_.empty()) { if (!format_before_.empty()) {
auto txt = fmt::format(format_before_, auto txt = fmt::format(format_before_,
fmt::arg("title", title), fmt::arg("title", title),
fmt::arg("name", name),
fmt::arg("app_id", app_id), fmt::arg("app_id", app_id),
fmt::arg("state", state_string()), fmt::arg("state", state_string()),
fmt::arg("short_state", state_string(true)) fmt::arg("short_state", state_string(true))
@ -585,6 +621,7 @@ void Task::update()
if (!format_after_.empty()) { if (!format_after_.empty()) {
auto txt = fmt::format(format_after_, auto txt = fmt::format(format_after_,
fmt::arg("title", title), fmt::arg("title", title),
fmt::arg("name", name),
fmt::arg("app_id", app_id), fmt::arg("app_id", app_id),
fmt::arg("state", state_string()), fmt::arg("state", state_string()),
fmt::arg("short_state", state_string(true)) fmt::arg("short_state", state_string(true))
@ -599,6 +636,7 @@ void Task::update()
if (!format_tooltip_.empty()) { if (!format_tooltip_.empty()) {
auto txt = fmt::format(format_tooltip_, auto txt = fmt::format(format_tooltip_,
fmt::arg("title", title), fmt::arg("title", title),
fmt::arg("name", name),
fmt::arg("app_id", app_id), fmt::arg("app_id", app_id),
fmt::arg("state", state_string()), fmt::arg("state", state_string()),
fmt::arg("short_state", state_string(true)) fmt::arg("short_state", state_string(true))
@ -726,6 +764,15 @@ Taskbar::Taskbar(const std::string &id, const waybar::Bar &bar, const Json::Valu
} }
} }
// Load app_id remappings
if (config_["app_ids-mapping"].isObject()) {
const Json::Value& mapping = config_["app_ids-mapping"];
const std::vector<std::string> app_ids = config_["app_ids-mapping"].getMemberNames();
for (auto& app_id : app_ids) {
app_ids_replace_map_.emplace(app_id, mapping[app_id].asString());
}
}
icon_themes_.push_back(Gtk::IconTheme::get_default()); icon_themes_.push_back(Gtk::IconTheme::get_default());
} }
@ -857,10 +904,10 @@ bool Taskbar::all_outputs() const
return config_["all-outputs"].isBool() && config_["all-outputs"].asBool(); return config_["all-outputs"].isBool() && config_["all-outputs"].asBool();
} }
std::vector<Glib::RefPtr<Gtk::IconTheme>> Taskbar::icon_themes() const const std::vector<Glib::RefPtr<Gtk::IconTheme>>& Taskbar::icon_themes() const { return icon_themes_; }
{
return icon_themes_; const std::unordered_set<std::string>& Taskbar::ignore_list() const { return ignore_list_; }
}
const std::unordered_set<std::string> &Taskbar::ignore_list() const { return ignore_list_; } const std::map<std::string, std::string>& Taskbar::app_ids_replace_map() const { return app_ids_replace_map_; }
} /* namespace waybar::modules::wlr */ } /* namespace waybar::modules::wlr */