image module reworked and added multiple image rendering capability
This commit is contained in:
@@ -14,6 +14,64 @@
|
||||
|
||||
namespace waybar::modules {
|
||||
|
||||
namespace image {
|
||||
|
||||
class IStrategy {
|
||||
public:
|
||||
virtual ~IStrategy() = default;
|
||||
virtual void update() = 0;
|
||||
};
|
||||
|
||||
class SingleImageStrategy : public IStrategy {
|
||||
public:
|
||||
SingleImageStrategy(const std::string &, const Json::Value &, const std::string &,
|
||||
Gtk::EventBox &, bool);
|
||||
~SingleImageStrategy() override = default;
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
void parseOutputRaw();
|
||||
|
||||
util::command::res output_;
|
||||
Json::Value config_;
|
||||
Gtk::Image image_;
|
||||
std::string path_;
|
||||
std::string tooltip_;
|
||||
int size_;
|
||||
Gtk::Box box_;
|
||||
bool hasTooltip_;
|
||||
};
|
||||
|
||||
class MultipleImageStrategy : public IStrategy {
|
||||
public:
|
||||
MultipleImageStrategy(const std::string &, const Json::Value &, const std::string &,
|
||||
Gtk::EventBox &);
|
||||
~MultipleImageStrategy() override = default;
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
struct ImageData {
|
||||
std::string path;
|
||||
std::string marker;
|
||||
std::string tooltip;
|
||||
std::string on_click;
|
||||
std::shared_ptr<Gtk::Image> img;
|
||||
std::shared_ptr<Gtk::Button> btn;
|
||||
};
|
||||
|
||||
void setImagesData(const Json::Value &);
|
||||
void setupAndDraw();
|
||||
void resetBoxAndMemory();
|
||||
void handleClick(const Glib::ustring &data);
|
||||
|
||||
Json::Value config_;
|
||||
int size_;
|
||||
Gtk::Box box_;
|
||||
std::vector<ImageData> images_data_;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
|
||||
class Image : public AModule {
|
||||
public:
|
||||
Image(const std::string &, const Json::Value &);
|
||||
@@ -24,16 +82,11 @@ class Image : public AModule {
|
||||
private:
|
||||
void delayWorker();
|
||||
void handleEvent();
|
||||
void parseOutputRaw();
|
||||
static std::unique_ptr<image::IStrategy> getStrategy(const std::string &, const Json::Value &,
|
||||
const std::string &, Gtk::EventBox &, bool);
|
||||
|
||||
Gtk::Box box_;
|
||||
Gtk::Image image_;
|
||||
std::string path_;
|
||||
std::string tooltip_;
|
||||
int size_;
|
||||
int interval_;
|
||||
util::command::res output_;
|
||||
|
||||
std::unique_ptr<image::IStrategy> strategy_;
|
||||
util::SleeperThread thread_;
|
||||
};
|
||||
|
||||
|
||||
+156
-1
@@ -6,10 +6,19 @@ waybar - image module
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The *image* module displays an image from a path.
|
||||
The *image* module displays container of images(or image) from a provided paths
|
||||
|
||||
# REMARK
|
||||
|
||||
This module has been rewritten to add multiple image rendering functionality.
|
||||
To avoid users inconvenience of breaking changes, everything related to old(only
|
||||
one *image*) implementation has been left untouched including configuration part. For this
|
||||
reason this wiki page is split into two parts
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
For single *image*
|
||||
|
||||
*path*: ++
|
||||
typeof: string ++
|
||||
The path to the image.
|
||||
@@ -88,3 +97,149 @@ $path\\n$tooltip
|
||||
|
||||
- *#image*
|
||||
- *#image.empty*
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
For multiple *image*
|
||||
|
||||
*size*: ++
|
||||
typeof: string ++
|
||||
default: 16 ++
|
||||
Minumum size of the rendered image in pixels.
|
||||
|
||||
*interval*: ++
|
||||
typeof: interger ++
|
||||
default: INT_MAX ++
|
||||
The interval in seconds to redraw module ++
|
||||
Default value is max value of int and changing it is only recommended ++
|
||||
if image path or other property is being changed overtime ++
|
||||
If no *interval* is provided, the module will only be rendered once.
|
||||
|
||||
*multiple*: ++
|
||||
typeof: bool ++
|
||||
default: false ++
|
||||
this parameter is used to decide if old *image* implementation should be ++
|
||||
used(therefore render only one image) or to pick a new implementation to ++
|
||||
rended multiple images provided from this config. default value of this ++
|
||||
parameter is false.
|
||||
|
||||
*signal*: ++
|
||||
typeof: interger ++
|
||||
default: 0 ++
|
||||
The signal number is used to redraw the module. ++
|
||||
This is used if *interval* is not provided and module should change ++
|
||||
regularly depended on external events, e.g when clicking on it. ++
|
||||
The provided value is valid between 1 and N, where SIGRTMIN+N <= SIGRTMAX. ++
|
||||
e.g if *signal* = 7, 34 + 7 = 41, which means waybar process should ++
|
||||
receive signal 41 to redraw this module, since SIGRTMIN = 34 (see 'kill -l').
|
||||
|
||||
*entries*: ++
|
||||
typeof: array ++
|
||||
default: empty array ++
|
||||
Json array of objects consisting of several fields ++
|
||||
this is a main part of configuration where per image properties are defined
|
||||
|
||||
*path*: ++
|
||||
typeof: string ++
|
||||
default: empty string ++
|
||||
file path of picture to draw
|
||||
|
||||
*marker*: ++
|
||||
typeof: string ++
|
||||
default: empty string ++
|
||||
user defined per image keyword(marker) to identify them, e.g in styles.css ++
|
||||
e.g if you want to draw 3 pictures and one of them dimmed, you can give ++
|
||||
*marker*: "dimmed" value and in styles.css write something like this ++
|
||||
#image .dimmed { opacity: 0.3 }
|
||||
|
||||
*tooltip*: ++
|
||||
typeof: string ++
|
||||
default: empty string ++
|
||||
description of the image displayed on hover
|
||||
|
||||
*on-click*: ++
|
||||
typeof: string ++
|
||||
default: empty string ++
|
||||
action to perform when clicking on the image. ++
|
||||
The action can be any system or custom binary/script ++
|
||||
e.g if { "on-click": "notify-send \"hello world\"" } ++
|
||||
"hello world" will appear as notification.
|
||||
|
||||
*exec*: ++
|
||||
typeof: string ++
|
||||
default: empty string ++
|
||||
same as *entries* but provided from external script which should dynamically ++
|
||||
return json array of objects consisting of same keys as *entries*. ++
|
||||
This option is specifically useful in combination with *signal* option without *interval* provided.
|
||||
|
||||
# Examples
|
||||
|
||||
With entries
|
||||
```
|
||||
"image#wentr": {
|
||||
"entries": [
|
||||
{
|
||||
"path": "/home/user/Pictures/idk1.png",
|
||||
"marker": "dimmed",
|
||||
"tooltip": "dimmed image",
|
||||
"on-click": "notify-send \"hello world\""
|
||||
},
|
||||
{
|
||||
"path": "/home/user/Pictures/idk2.png",
|
||||
"marker": "normal",
|
||||
"on-click": "myCustomScript.py"
|
||||
}
|
||||
],
|
||||
"size": 32,
|
||||
"interval": 1 // will redraw every 5 seconds
|
||||
}
|
||||
```
|
||||
|
||||
With exec
|
||||
```
|
||||
"image#wexec": {
|
||||
"exec": "imageProvider.py",
|
||||
"size": 32,
|
||||
"signal": 7 // e.g 'pkill -n waybar --signal 41' will trigger redraw
|
||||
}
|
||||
```
|
||||
|
||||
where imageProvider.py looks like this
|
||||
```
|
||||
#!/usr/bin/python
|
||||
|
||||
import json
|
||||
|
||||
dir = "/home/user/Pictures/"
|
||||
out = [
|
||||
{"path": dir + "idk1.png", "marker": "clickable", "on-click": "echo 'hello world'"},
|
||||
{"path": dir + "idk2.png", "marker": "normal", "tooltip": "sample tooltip"},
|
||||
{"path": dir + "idk3.png", "marker": "important"},
|
||||
]
|
||||
res = json.dumps(out)
|
||||
print(res)
|
||||
```
|
||||
|
||||
## Example styles
|
||||
```
|
||||
#image .important {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
#image button.important {
|
||||
opacity: 0.2
|
||||
}
|
||||
|
||||
#image .normal {
|
||||
opacity: 0.3
|
||||
}
|
||||
```
|
||||
|
||||
# STYLE
|
||||
|
||||
- *#image*
|
||||
- *#image*.empty /\*when image could not be rendered\*/
|
||||
- *#image*.button /\*when on-click is provided\*/
|
||||
- *#image*.*marker* /\*where *marker* is parameter from config\*/
|
||||
- *#image*.button.*marker*
|
||||
|
||||
|
||||
+196
-20
@@ -1,25 +1,13 @@
|
||||
#include "modules/image.hpp"
|
||||
|
||||
#include <json/value.h>
|
||||
|
||||
waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
|
||||
: AModule(config, "image", id), box_(Gtk::ORIENTATION_HORIZONTAL, 0) {
|
||||
box_.pack_start(image_);
|
||||
box_.set_name("image");
|
||||
if (!id.empty()) {
|
||||
box_.get_style_context()->add_class(id);
|
||||
}
|
||||
box_.get_style_context()->add_class(MODULE_CLASS);
|
||||
event_box_.add(box_);
|
||||
|
||||
dp.emit();
|
||||
|
||||
size_ = config["size"].asInt();
|
||||
: AModule(config, "image", id) {
|
||||
strategy_ = getStrategy(id, config, MODULE_CLASS, event_box_, tooltipEnabled());
|
||||
|
||||
interval_ = config_["interval"].asInt();
|
||||
|
||||
if (size_ == 0) {
|
||||
size_ = 16;
|
||||
}
|
||||
|
||||
if (interval_ == 0) {
|
||||
interval_ = INT_MAX;
|
||||
}
|
||||
@@ -27,6 +15,20 @@ waybar::modules::Image::Image(const std::string& id, const Json::Value& config)
|
||||
delayWorker();
|
||||
}
|
||||
|
||||
auto waybar::modules::Image::getStrategy(
|
||||
const std::string& id, const Json::Value& cfg, const std::string& module, Gtk::EventBox& evbox,
|
||||
bool hasTooltip) -> std::unique_ptr<waybar::modules::image::IStrategy> {
|
||||
std::unique_ptr<waybar::modules::image::IStrategy> strat;
|
||||
if (!cfg["multiple"].empty() && cfg["multiple"].asBool()) {
|
||||
strat = std::make_unique<waybar::modules::image::MultipleImageStrategy>(id, cfg, module, evbox);
|
||||
} else {
|
||||
strat = std::make_unique<waybar::modules::image::SingleImageStrategy>(id, cfg, module, evbox,
|
||||
hasTooltip);
|
||||
}
|
||||
|
||||
return strat;
|
||||
}
|
||||
|
||||
void waybar::modules::Image::delayWorker() {
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
@@ -42,6 +44,180 @@ void waybar::modules::Image::refresh(int sig) {
|
||||
}
|
||||
|
||||
auto waybar::modules::Image::update() -> void {
|
||||
strategy_->update();
|
||||
|
||||
AModule::update();
|
||||
}
|
||||
|
||||
namespace waybar::modules::image {
|
||||
|
||||
MultipleImageStrategy::MultipleImageStrategy(const std::string& id, const Json::Value& config,
|
||||
const std::string& module, Gtk::EventBox& evbox)
|
||||
: IStrategy(), box_(Gtk::ORIENTATION_HORIZONTAL, 0) {
|
||||
config_ = config;
|
||||
|
||||
box_.set_name("image");
|
||||
box_.get_style_context()->add_class(id);
|
||||
box_.get_style_context()->add_class(module);
|
||||
evbox.add(box_);
|
||||
|
||||
size_ = config["size"].asInt();
|
||||
if (size_ == 0) {
|
||||
size_ = 16;
|
||||
}
|
||||
}
|
||||
|
||||
void MultipleImageStrategy::update() {
|
||||
// spdlog::info("update function run");
|
||||
|
||||
// clear box_, previous css classes and memory
|
||||
if (box_.get_children().size() > 0) {
|
||||
resetBoxAndMemory();
|
||||
}
|
||||
|
||||
// set new images from config script
|
||||
if (!config_["entries"].empty()) {
|
||||
setImagesData(config_["entries"]);
|
||||
} else if (!config_["exec"].empty()) {
|
||||
auto exec = util::command::exec(config_["exec"].asString(), "");
|
||||
Json::Value as_json;
|
||||
Json::Reader reader;
|
||||
|
||||
if (!reader.parse(exec.out, as_json)) {
|
||||
spdlog::error("invalid json from exec {}", exec.out);
|
||||
return;
|
||||
}
|
||||
|
||||
setImagesData(as_json);
|
||||
} else {
|
||||
spdlog::error("no image files provded in config");
|
||||
return;
|
||||
}
|
||||
|
||||
setupAndDraw();
|
||||
}
|
||||
|
||||
void MultipleImageStrategy::setupAndDraw() {
|
||||
for (unsigned int i = 0; i < images_data_.size(); i++) {
|
||||
images_data_[i].img = std::make_shared<Gtk::Image>();
|
||||
images_data_[i].btn = std::make_shared<Gtk::Button>();
|
||||
|
||||
auto img = images_data_[i].img;
|
||||
auto data = images_data_[i];
|
||||
|
||||
auto path = data.path;
|
||||
auto marker = data.marker;
|
||||
auto tooltip = data.tooltip;
|
||||
bool has_onclick = !data.on_click.empty();
|
||||
|
||||
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
||||
pixbuf = Gdk::Pixbuf::create_from_file(path, size_, size_);
|
||||
|
||||
if (has_onclick) {
|
||||
auto btn = images_data_[i].btn;
|
||||
btn->set_name("button_" + path);
|
||||
btn->get_style_context()->add_class(marker);
|
||||
btn->set_tooltip_text(tooltip);
|
||||
btn->set_image(*img);
|
||||
box_.pack_start(*btn);
|
||||
|
||||
btn->add_events(Gdk::BUTTON_PRESS_MASK);
|
||||
btn->signal_clicked().connect(
|
||||
sigc::bind(sigc::mem_fun(*this, &MultipleImageStrategy::handleClick), data.on_click));
|
||||
|
||||
if (pixbuf) {
|
||||
btn->show_all();
|
||||
img->set(pixbuf);
|
||||
box_.get_style_context()->remove_class("empty");
|
||||
} else {
|
||||
btn->hide();
|
||||
img->clear();
|
||||
img->hide();
|
||||
box_.get_style_context()->add_class("empty");
|
||||
}
|
||||
} else {
|
||||
img->set_name(path);
|
||||
img->get_style_context()->add_class(marker);
|
||||
img->set_tooltip_text(tooltip);
|
||||
box_.pack_start(*img);
|
||||
// spdlog::info("added image -> {}:{}", marker, path);
|
||||
|
||||
if (pixbuf) {
|
||||
img->set(pixbuf);
|
||||
img->show();
|
||||
box_.get_style_context()->remove_class("empty");
|
||||
} else {
|
||||
img->clear();
|
||||
img->hide();
|
||||
box_.get_style_context()->add_class("empty");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultipleImageStrategy::setImagesData(const Json::Value& entries) {
|
||||
for (unsigned int i = 0; i < entries.size(); i++) {
|
||||
auto path = entries[i]["path"];
|
||||
auto marker = entries[i]["marker"];
|
||||
auto tooltip = entries[i]["tooltip"];
|
||||
auto onclick = entries[i]["on-click"];
|
||||
|
||||
bool has_tooltip_err = !tooltip.empty() && !tooltip.isString();
|
||||
bool has_onclick_err = !onclick.empty() && !onclick.isString();
|
||||
|
||||
if (!path.isString() || !marker.isString() || has_tooltip_err || has_onclick_err ||
|
||||
!Glib::file_test(path.asString(), Glib::FILE_TEST_EXISTS)) {
|
||||
spdlog::error("invalid input in images config -> {}", entries[i]);
|
||||
return;
|
||||
}
|
||||
ImageData data;
|
||||
data.path = path.asString();
|
||||
data.marker = marker.asString();
|
||||
data.tooltip = !tooltip.empty() ? tooltip.asString() : "";
|
||||
data.on_click = onclick.asString();
|
||||
|
||||
images_data_.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
void MultipleImageStrategy::resetBoxAndMemory() {
|
||||
auto children = box_.get_children();
|
||||
for (auto child : children) {
|
||||
box_.remove(*child);
|
||||
// spdlog::info("child removed with name -> {}", std::string(child->get_name()));
|
||||
}
|
||||
|
||||
images_data_.clear();
|
||||
}
|
||||
|
||||
void MultipleImageStrategy::handleClick(const Glib::ustring& data) {
|
||||
auto msg = std::string(data);
|
||||
|
||||
auto exec = util::command::exec(data, "");
|
||||
}
|
||||
|
||||
SingleImageStrategy::SingleImageStrategy(const std::string& id, const Json::Value& config,
|
||||
const std::string& module, Gtk::EventBox& evbox,
|
||||
bool tooltipEnabled)
|
||||
: IStrategy(), box_(Gtk::ORIENTATION_HORIZONTAL, 0) {
|
||||
config_ = config;
|
||||
hasTooltip_ = tooltipEnabled;
|
||||
|
||||
box_.pack_start(image_);
|
||||
box_.set_name("image");
|
||||
if (!id.empty()) {
|
||||
box_.get_style_context()->add_class(id);
|
||||
}
|
||||
box_.get_style_context()->add_class(module);
|
||||
evbox.add(box_);
|
||||
|
||||
size_ = config["size"].asInt();
|
||||
if (size_ == 0) {
|
||||
size_ = 16;
|
||||
}
|
||||
}
|
||||
|
||||
void SingleImageStrategy::update() {
|
||||
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
||||
if (config_["path"].isString()) {
|
||||
path_ = config_["path"].asString();
|
||||
@@ -57,7 +233,7 @@ auto waybar::modules::Image::update() -> void {
|
||||
pixbuf = {};
|
||||
|
||||
if (pixbuf) {
|
||||
if (tooltipEnabled() && !tooltip_.empty()) {
|
||||
if (hasTooltip_ && !tooltip_.empty()) {
|
||||
if (box_.get_tooltip_markup() != tooltip_) {
|
||||
box_.set_tooltip_markup(tooltip_);
|
||||
}
|
||||
@@ -70,11 +246,9 @@ auto waybar::modules::Image::update() -> void {
|
||||
image_.hide();
|
||||
box_.get_style_context()->add_class("empty");
|
||||
}
|
||||
|
||||
AModule::update();
|
||||
}
|
||||
|
||||
void waybar::modules::Image::parseOutputRaw() {
|
||||
void SingleImageStrategy::parseOutputRaw() {
|
||||
std::istringstream output(output_.out);
|
||||
std::string line;
|
||||
int i = 0;
|
||||
@@ -89,3 +263,5 @@ void waybar::modules::Image::parseOutputRaw() {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace waybar::modules::image
|
||||
|
||||
Reference in New Issue
Block a user