Repository navigation
Expand file tree
/
Copy pathAGraph.cpp
More file actions
302 lines (252 loc) · 10.1 KB
/
Copy pathAGraph.cpp
File metadata and controls
302 lines (252 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
8000
div>
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#include "AGraph.hpp"
#include <cairomm/context.h>
#include <fmt/format.h>
#include <algorithm>
#include <cmath>
#include <fstream>
#include <iostream>
#include <util/command.hpp>
#include "config.hpp"
namespace waybar {
AGraph::AGraph(const Json::Value& config, const std::string& name, const std::string& id,
uint16_t interval, bool enable_click, bool enable_scroll)
: AModule(config, name, id,
config["format-alt"].isString() || config["menu"].isString() || enable_click,
enable_scroll),
interval_(
config_["interval"] == "once"
? std::chrono::milliseconds::max()
: std::chrono::milliseconds(
config_["interval"].isNumeric()
? std::max(1L, static_cast<long>(config_["interval"].asDouble() * 1000))
: 1000L * static_cast<long>(interval))) {
graph_.signal_draw().connect(sigc::mem_fun(*this, &AGraph::onDraw));
graph_.set_name(name);
if (!id.empty()) {
graph_.get_style_context()->add_class(id);
}
graph_.get_style_context()->add_class(MODULE_CLASS);
if (config_["width"].isUInt()) {
graph_.set_size_request(config_["width"].asUInt(), -1);
} else {
graph_.set_size_request(100, -1);
}
event_box_.add(graph_);
if (config_["datapoints"].isUInt() && config_["datapoints"].asUInt() > 0) {
datapoints_ = config_["datapoints"].asUInt();
}
if (config_["graph_type"].isString()) {
std::string type = config_["graph_type"].asString();
if (type == "line") {
graph_type_ = GraphType::LINE;
} else if (type == "bar") {
graph_type_ = GraphType::BAR;
} else if (type == "gauge") {
graph_type_ = GraphType::GAUGE;
}
}
// If a GTKMenu is requested in the config
if (config_["menu"].isString()) {
// Create the GTKMenu widget
try {
// Check that the file exists
std::string menuFile = config_["menu-file"].asString();
// there might be "~" or "$HOME" in original path, try to expand it.
auto result = Config::tryExpandPath(menuFile, "");
if (result.empty()) {
throw std::runtime_error("Failed to expand file: " + menuFile);
}
menuFile = result.front();
// Read the menu descriptor file
std::ifstream file(menuFile);
if (!file.is_open()) {
throw std::runtime_error("Failed to open file: " + menuFile);
}
std::stringstream fileContent;
fileContent << file.rdbuf();
GtkBuilder* builder = gtk_builder_new();
// Make the GtkBuilder and check for errors in his parsing
if (gtk_builder_add_from_string(builder, fileContent.str().c_str(), -1, nullptr) == 0U) {
throw std::runtime_error("Error found in the file " + menuFile);
}
menu_ = gtk_builder_get_object(builder, "menu");
if (menu_ == nullptr) {
throw std::runtime_error("Failed to get 'menu' object from GtkBuilder");
}
submenus_ = std::map<std::string, GtkMenuItem*>();
menuActionsMap_ = std::map<std::string, std::string>();
// Linking actions to the GTKMenu based on
for (Json::Value::const_iterator it = config_["menu-actions"].begin();
it != config_["menu-actions"].end(); ++it) {
std::string key = it.key().asString();
submenus_[key] = GTK_MENU_ITEM(gtk_builder_get_object(builder, key.c_str()));
menuActionsMap_[key] = it->asString();
g_signal_connect(submenus_[key], "activate", G_CALLBACK(handleGtkMenuEvent),
(gpointer)menuActionsMap_[key].c_str());
}
} catch (std::runtime_error& e) {
spdlog::warn("Error while creating the menu : {}. Menu popup not activated.", e.what());
}
}
}
auto AGraph::update() -> void {
graph_.queue_draw();
AModule::update();
}
void AGraph::handleGtkMenuEvent(GtkMenuItem* /*menuitem*/, gpointer data) {
waybar::util::command::res res = waybar::util::command::exec((char*)data, "GtkMenu");
}
void AGraph::addValue(const int n) {
if (datapoints_ > 0 && values_.size() >= datapoints_) {
values_.pop_front();
}
values_.push_back(n);
}
bool AGraph::onDraw(const Cairo::RefPtr<Cairo::Context>& cr) {
const int width = graph_.get_allocated_width();
const int height = graph_.get_allocated_height() - 1;
if (values_.empty() || width <= 0 || height <= 0) {
return false;
}
auto style_context = graph_.get_style_context();
Gdk::RGBA fg_color = style_context->get_color(Gtk::STATE_FLAG_NORMAL);
Gdk::RGBA bg_color = fg_color;
bg_color.set_alpha(0.3);
cr->set_line_width(1.0);
const double step_width = static_cast<double>(width) / datapoints_;
const int values_count = values_.size();
const int empty_space = datapoints_ - values_count;
std::vector<std::pair<double, double>> points;
points.reserve(values_count);
for (int i = empty_space; i < datapoints_; ++i) {
double x = i * step_width;
int value_index = i - empty_space;
int value = values_[value_index];
double y = height - (static_cast<double>(value) / 100.0 * height);
points.emplace_back(x, y);
}
if (!points.empty()) {
switch (graph_type_) {
case GraphType::LINE:
drawFilledArea(cr, points, height, bg_color);
drawLine(cr, points, fg_color);
break;
case GraphType::BAR:
drawBars(cr, width, height, values_.empty() ? 0 : values_.back(), fg_color);
break;
case GraphType::GAUGE:
drawGauge(cr, width, height, values_.empty() ? 0 : values_.back(), fg_color);
break;
}
}
return false;
}
void AGraph::drawFilledArea(const Cairo::RefPtr<Cairo::Context>& cr,
const std::vector<std::pair<double, double>>& points, double height,
const Gdk::RGBA& bg_color) {
if (points.empty()) return;
double first_x = points.front().first;
double last_x = points.back().first;
drawPath(cr, points);
cr->line_to(last_x, height);
cr->line_to(first_x, height);
cr->close_path();
cr->set_source_rgba(bg_color.get_red(), bg_color.get_green(), bg_color.get_blue(),
bg_color.get_alpha());
cr->fill();
}
void AGraph::drawLine(const Cairo::RefPtr<Cairo::Context>& cr,
const std::vector<std::pair<double, double>>& points,
const Gdk::RGBA& fg_color) {
if (points.empty()) return;
cr->begin_new_path();
drawPath(cr, points);
cr->set_source_rgba(fg_color.get_red(), fg_color.get_green(), fg_color.get_blue(),
fg_color.get_alpha());
cr->stroke();
}
void AGraph::drawPath(const Cairo::RefPtr<Cairo::Context>& cr,
const std::vector<std::pair<double, double>>& points) {
if (points.empty()) return;
bool first_point = true;
for (const auto& point : points) {
if (first_point) {
cr->move_to(point.first, point.second);
first_point = false;
} else {
cr->line_to(point.first, point.second);
}
}
}
void AGraph::drawBars(const Cairo::RefPtr<Cairo::Context>& cr, double width, double height,
int current_value, const Gdk::RGBA& fg_color) {
current_value = std::min(100, std::max(0, current_value));
double green_height = height * (std::min(current_value, 40) / 100.0);
cr->set_source_rgba(fg_color.get_red(), fg_color.get_green(), fg_color.get_blue(), 0.5);
cr->rectangle(0, height - green_height, width, green_height);
cr->fill();
if (current_value > 40) {
double yellow_height = height * (std::min(current_value, 75) - 40) / 100.0;
cr->set_source_rgba(fg_color.get_red(), fg_color.get_green(), fg_color.get_blue(), 0.7);
cr->rectangle(0, height - green_height - yellow_height, width, yellow_height);
cr->fill();
}
if (current_value > 75) {
double orange_height = height * (std::min(current_value, 85) - 75) / 100.0;
cr->set_source_rgba(fg_color.get_red(), fg_color.get_green(), fg_color.get_blue(), 0.85);
double yellow_height = height * (std::min(current_value, 75) - 40) / 100.0;
cr->rectangle(0, height - green_height - yellow_height - orange_height, width, orange_height);
cr->fill();
}
if (current_value > 85) {
double red_height = height * (current_value - 85) / 100.0;
cr->set_source_rgba(fg_color.get_red(), fg_color.get_green(), fg_color.get_blue(), 1.0);
double yellow_height = height * (std::min(current_value, 75) - 40) / 100.0;
double orange_height = height * (std::min(current_value, 85) - 75) / 100.0;
cr->rectangle(0, height - green_height - yellow_height - orange_height - red_height, width,
red_height);
cr->fill();
}
double value_height = height * (current_value / 100.0);
cr->set_source_rgba(0.2, 0.2, 0.2, 0.8);
cr->rectangle(0, height - value_height, width, 2);
cr->fill();
}
void AGraph::drawGauge(const Cairo::RefPtr<Cairo::Context>& cr, double width, double height,
int current_value, const Gdk::RGBA& fg_color) {
double center_x = width / 2.0;
double center_y = height;
double radius = height / 2.0;
cr->set_line_width(10.0);
double angle1 = M_PI;
double angle2 = angle1 + 0.3 * angle1;
// Green section (0-33%)
cr->set_source_rgba(fg_color.get_red(), fg_color.get_green(), fg_color.get_blue(), 0.5);
cr->arc(center_x, center_y, radius, angle1, angle2);
cr->stroke();
// Yellow section (33-66%)
angle1 = angle2;
angle2 = angle1 + 0.3 * angle1;
cr->set_source_rgba(fg_color.get_red(), fg_color.get_green(), fg_color.get_blue(), 0.75);
cr->arc(center_x, center_y, radius, angle1, angle2);
cr->stroke();
// Red section (66-100%)
angle1 = angle2;
angle2 = 0.0;
cr->set_source_rgba(fg_color.get_red(), fg_color.get_green(), fg_color.get_blue(), 1.0);
cr->arc(center_x, center_y, radius, angle1, angle2);
cr->stroke();
// Draw needle
double percentage = std::min(100, std::max(0, current_value)) / 100.0;
double needle_angle = M_PI * percentage;
double needle_length = radius;
double needle_x = center_x - needle_length * cos(needle_angle);
double needle_y = center_y - needle_length * sin(needle_angle);
cr->set_source_rgba(1.0, 1.0, 1.0, 1.0);
cr->set_line_width(2.0);
cr->begin_new_path();
cr->move_to(center_x, center_y);
cr->line_to(needle_x, needle_y);
cr->stroke();
}
} // namespace waybar
You can’t perform that action at this time.