diff --git a/include/cairo/context.hpp b/include/cairo/context.hpp index c9326ab4..156fc58f 100644 --- a/include/cairo/context.hpp +++ b/include/cairo/context.hpp @@ -11,6 +11,7 @@ #include "cairo/utils.hpp" #include "common.hpp" #include "components/logger.hpp" +#include "components/types.hpp" #include "errors.hpp" #include "utils/color.hpp" #include "utils/string.hpp" @@ -63,6 +64,9 @@ namespace cairo { } context& operator<<(const abspos& p) { + if (p.clear) { + cairo_new_path(m_c); + } cairo_move_to(m_c, p.x, p.y); return *this; } @@ -117,8 +121,24 @@ namespace cairo { return *this; } + context& operator<<(const rounded_corners& c) { + double radius = c.radius / 1.0; + double d = M_PI / 180.0; + cairo_new_sub_path(m_c); + cairo_arc(m_c, c.x + c.w - radius, c.y + radius, radius, -90 * d, 0 * d); + cairo_arc(m_c, c.x + c.w - radius, c.y + c.h - radius, radius, 0 * d, 90 * d); + cairo_arc(m_c, c.x + radius, c.y + c.h - radius, radius, 90 * d, 180 * d); + cairo_arc(m_c, c.x + radius, c.y + radius, radius, 180 * d, 270 * d); + cairo_close_path(m_c); + return *this; + } + context& operator<<(const textblock& t) { - // Sort the fontlist so that the preferred font is tested first + double x, y; + position(&x, &y); + + // Sort the fontlist so that the + // preferred font gets prioritized auto& fns = m_fonts; std::sort(fns.begin(), fns.end(), [&](const unique_ptr& a, const unique_ptr&) { if (t.fontindex > 0 && std::distance(fns.begin(), std::find(fns.begin(), fns.end(), a)) == t.fontindex - 1) { @@ -147,18 +167,56 @@ namespace cairo { end++; } - f->use(m_c); + // encapsulate the vert. centering + save(); + { + *this << abspos{x, y}; - cairo_text_extents_t extents; - f->textwidth(utf8, &extents); + f->use(); - save(); // encapsulate the y pos update - *this << relpos{0.0, extents.height / 2.0 - f->extents().descent + f->offset()}; - f->render(subset); + cairo_text_extents_t extents; + f->textwidth(utf8, &extents); + double dx{0.0}, dy{0.0}; + + if (t.align == alignment::CENTER) { + dx -= extents.x_advance / 2.0; + + save(); + { + cairo_rectangle(m_c, x, y, extents.x_advance / 2.0, extents.height); + auto pattern = cairo_pattern_create_rgba(1.0, 0.0, 0.0, 0.0); + cairo_set_source(m_c, pattern); + cairo_translate(m_c, dx, 0.0); + cairo_set_operator(m_c, CAIRO_OPERATOR_DEST); + cairo_fill(m_c); + cairo_pattern_destroy(pattern); + } + restore(); + } else if (t.align == alignment::RIGHT) { + dx -= extents.x_advance; + + save(); + { + cairo_rectangle(m_c, x, y, extents.x_advance, extents.height); + auto pattern = cairo_pattern_create_rgba(0.0, 0.0, 0.0, 0.0); + cairo_set_source(m_c, pattern); + cairo_set_operator(m_c, CAIRO_OPERATOR_DEST); + cairo_translate(m_c, dx, 0.0); + cairo_fill(m_c); + cairo_pattern_destroy(pattern); + } + restore(); + } + + auto fontextents = f->extents(); + f->render(subset, x + dx, + y + dy - (extents.height / 2.0 + extents.y_bearing + fontextents.descent) + f->offset()); + position(&x, nullptr); + x += dx; + y += dy; + } restore(); - *this << relpos{extents.width, 0.0}; - chars.erase(chars.begin(), end); break; } @@ -179,6 +237,8 @@ namespace cairo { chars.erase(chars.begin(), ++chars.begin()); } + *this << abspos{x, y}; + return *this; } @@ -197,11 +257,11 @@ namespace cairo { } context& restore(bool restore_point = true) { + cairo_restore(m_c); if (restore_point) { *this << abspos{m_points.front().first, m_points.front().first}; m_points.pop_front(); } - cairo_restore(m_c); return *this; } @@ -215,8 +275,12 @@ namespace cairo { return *this; } - context& fill() { - cairo_fill(m_c); + context& fill(bool preserve = false) { + if (preserve) { + cairo_fill_preserve(m_c); + } else { + cairo_fill(m_c); + } return *this; } @@ -231,10 +295,11 @@ namespace cairo { return *this; } - context& position(double* x, double* y) { - *x = 0.0; - *y = 0.0; + context& position(double* x, double* y = nullptr) { if (cairo_has_current_point(m_c)) { + double x_, y_; + x = x != nullptr ? x : &x_; + y = y != nullptr ? y : &y_; cairo_get_current_point(m_c, x, y); } return *this; diff --git a/include/cairo/font.hpp b/include/cairo/font.hpp index 9f75a624..4d49d40f 100644 --- a/include/cairo/font.hpp +++ b/include/cairo/font.hpp @@ -63,25 +63,23 @@ namespace cairo { class font { public: explicit font(cairo_t* cairo, double offset) : m_cairo(cairo), m_offset(offset) {} - virtual ~font() {}; + virtual ~font(){}; virtual string name() const = 0; virtual string file() const = 0; virtual double offset() const = 0; virtual double size() const = 0; - virtual void use(cairo_t* c) { - cairo_set_font_face(c, cairo_font_face_reference(m_font_face)); + virtual cairo_font_extents_t extents() = 0; + + virtual void use() { + cairo_set_font_face(m_cairo, cairo_font_face_reference(m_font_face)); } virtual size_t match(unicode_charlist& charlist) = 0; - virtual size_t render(const string& text) = 0; + virtual size_t render(const string& text, double x = 0.0, double y = 0.0, bool reverse = false) = 0; virtual void textwidth(const string& text, cairo_text_extents_t* extents) = 0; - cairo_font_extents_t extents() const { - return m_extents; - } - protected: cairo_t* m_cairo; cairo_font_face_t* m_font_face{nullptr}; @@ -111,7 +109,7 @@ namespace cairo { throw application_error(sstream() << "cairo_scaled_font_create(): " << cairo_status_to_string(status)); } - auto lock = make_unique(cairo_scaled_font_reference(m_scaled)); + auto lock = make_unique(m_scaled); auto face = static_cast(*lock); if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) == FT_Err_Ok) { @@ -121,6 +119,8 @@ namespace cairo { } else if (FT_Select_Charmap(face, FT_ENCODING_SJIS) == FT_Err_Ok) { return; } + + lock.reset(); } ~font_fc() override { @@ -132,6 +132,11 @@ namespace cairo { } } + cairo_font_extents_t extents() override { + cairo_scaled_font_extents(m_scaled, &m_extents); + return m_extents; + } + string name() const override { return property("family"); } @@ -157,12 +162,12 @@ namespace cairo { return px; } - void use(cairo_t* c) override { - cairo_set_scaled_font(c, cairo_scaled_font_reference(m_scaled)); + void use() override { + cairo_set_scaled_font(m_cairo, m_scaled); } size_t match(unicode_charlist& charlist) override { - auto lock = make_unique(cairo_scaled_font_reference(m_scaled)); + auto lock = make_unique(m_scaled); auto face = static_cast(*lock); size_t available_chars = 0; for (auto&& c : charlist) { @@ -176,20 +181,22 @@ namespace cairo { return available_chars; } - size_t render(const string& text) override { - double x, y; - cairo_get_current_point(m_cairo, &x, &y); - + size_t render(const string& text, double x = 0.0, double y = 0.0, bool reverse = false) override { cairo_glyph_t* glyphs{nullptr}; cairo_text_cluster_t* clusters{nullptr}; cairo_text_cluster_flags_t cf{}; int nglyphs = 0, nclusters = 0; + + if (reverse) { + cf = CAIRO_TEXT_CLUSTER_FLAG_BACKWARD; + } + string utf8 = string(text); - auto status = cairo_scaled_font_text_to_glyphs(cairo_scaled_font_reference(m_scaled), x, y, utf8.c_str(), - utf8.size(), &glyphs, &nglyphs, &clusters, &nclusters, &cf); + auto status = cairo_scaled_font_text_to_glyphs( + m_scaled, x, y, utf8.c_str(), utf8.size(), &glyphs, &nglyphs, &clusters, &nclusters, &cf); if (status != CAIRO_STATUS_SUCCESS) { - throw application_error(sstream() << "cairo_scaled_font_status()" << cairo_status_to_string(status)); + throw application_error(sstream() << "cairo_scaled_font_text_to_glyphs()" << cairo_status_to_string(status)); } size_t bytes = 0; @@ -201,18 +208,36 @@ namespace cairo { } } - if (bytes) { + if (bytes && bytes < text.size()) { + cairo_glyph_free(glyphs); + cairo_text_cluster_free(clusters); + utf8 = text.substr(0, bytes); - cairo_scaled_font_text_to_glyphs( + auto status = cairo_scaled_font_text_to_glyphs( m_scaled, x, y, utf8.c_str(), utf8.size(), &glyphs, &nglyphs, &clusters, &nclusters, &cf); - cairo_show_text_glyphs(m_cairo, utf8.c_str(), utf8.size(), glyphs, nglyphs, clusters, nclusters, cf); + + if (status != CAIRO_STATUS_SUCCESS) { + throw application_error(sstream() << "cairo_scaled_font_text_to_glyphs()" << cairo_status_to_string(status)); + } } + if (bytes) { + cairo_text_extents_t extents{}; + cairo_scaled_font_glyph_extents(m_scaled, glyphs, nglyphs, &extents); + cairo_show_text_glyphs(m_cairo, utf8.c_str(), utf8.size(), glyphs, nglyphs, clusters, nclusters, cf); + cairo_rel_move_to(m_cairo, extents.x_advance, 0.0); + // cairo_glyph_path(m_cairo, glyphs, nglyphs); + cairo_fill_preserve(m_cairo); + } + + cairo_glyph_free(glyphs); + cairo_text_cluster_free(clusters); + return bytes; } void textwidth(const string& text, cairo_text_extents_t* extents) override { - cairo_scaled_font_text_extents(cairo_scaled_font_reference(m_scaled), text.c_str(), extents); + cairo_scaled_font_text_extents(m_scaled, text.c_str(), extents); } protected: diff --git a/include/cairo/types.hpp b/include/cairo/types.hpp index 5313d986..d5dccbd1 100644 --- a/include/cairo/types.hpp +++ b/include/cairo/types.hpp @@ -4,10 +4,13 @@ POLYBAR_NS +enum class alignment; + namespace cairo { struct abspos { double x; double y; + bool clear{true}; }; struct relpos { double x; @@ -37,9 +40,18 @@ namespace cairo { vector steps; }; + struct rounded_corners { + double x; + double y; + double w; + double h; + double radius; + }; + struct textblock { + alignment align; string contents; - unsigned char fontindex; + int fontindex; }; } diff --git a/include/components/types.hpp b/include/components/types.hpp index 7e20696d..dc55c1e2 100644 --- a/include/components/types.hpp +++ b/include/components/types.hpp @@ -145,6 +145,7 @@ struct bar_settings { std::unordered_map borders{}; + double radius{0.0}; int spacing{0}; string separator{}; diff --git a/src/components/bar.cpp b/src/components/bar.cpp index 843cbed2..ec2eb2fa 100644 --- a/src/components/bar.cpp +++ b/src/components/bar.cpp @@ -136,6 +136,7 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const m_opts.spacing = m_conf.get(bs, "spacing", m_opts.spacing); m_opts.separator = m_conf.get(bs, "separator", ""s); m_opts.locale = m_conf.get(bs, "locale", ""s); + m_opts.radius = m_conf.get(bs, "radius", m_opts.radius); try { auto padding = m_conf.get(bs, "module-padding"); diff --git a/src/components/renderer.cpp b/src/components/renderer.cpp index 29a392f4..d7845d48 100644 --- a/src/components/renderer.cpp +++ b/src/components/renderer.cpp @@ -256,8 +256,8 @@ void renderer::flush() { /** * Reserve space at given edge */ -void renderer::reserve_space(edge side, unsigned short int w) { - m_log.trace_x("renderer: reserve_space(%i, %i)", static_cast(side), w); +void renderer::reserve_space(edge side, unsigned int w) { + m_log.trace_x("renderer: reserve_space(%i, %i)", static_cast(side), w); m_cleararea.side = side; m_cleararea.size = w; @@ -295,6 +295,16 @@ void renderer::fill_background() { m_context->save(); *m_context << m_compositing_background; + if (m_bar.radius != 0.0) { + // clang-format off + *m_context << cairo::rounded_corners{ + static_cast(m_rect.x), + static_cast(m_rect.y), + static_cast(m_rect.width), + static_cast(m_rect.height), m_bar.radius}; + // clang-format on + } + if (!m_bar.background_steps.empty()) { m_log.trace_x("renderer: gradient background (steps=%lu)", m_bar.background_steps.size()); *m_context << cairo::linear_gradient{0.0, 0.0 + m_rect.y, 0.0, 0.0 + m_rect.height, m_bar.background_steps}; @@ -303,7 +313,12 @@ void renderer::fill_background() { *m_context << m_bar.background; } - m_context->paint(); + if (m_bar.radius != 0.0) { + m_context->fill(); + } else { + m_context->paint(); + } + m_context->restore(); } @@ -311,8 +326,8 @@ void renderer::fill_background() { * Fill overline color */ void renderer::fill_overline(double x, double w) { - if (m_bar.overline.size && m_attributes.test(static_cast(attribute::OVERLINE))) { - m_log.trace_x("renderer: overline(x=%i, w=%i)", x, w); + if (m_bar.overline.size && m_attributes.test(static_cast(attribute::OVERLINE))) { + m_log.trace_x("renderer: overline(x=%f, w=%f)", x, w); m_context->save(); *m_context << m_compositing_overline; *m_context << m_color_overline; @@ -326,8 +341,8 @@ void renderer::fill_overline(double x, double w) { * Fill underline color */ void renderer::fill_underline(double x, double w) { - if (m_bar.underline.size && m_attributes.test(static_cast(attribute::UNDERLINE))) { - m_log.trace_x("renderer: underline(x=%i, w=%i)", x, w); + if (m_bar.underline.size && m_attributes.test(static_cast(attribute::UNDERLINE))) { + m_log.trace_x("renderer: underline(x=%f, w=%f)", x, w); m_context->save(); *m_context << m_compositing_underline; *m_context << m_color_underline; @@ -382,21 +397,15 @@ void renderer::fill_borders() { void renderer::draw_text(const string& contents) { m_log.trace_x("renderer: text(%s)", contents.c_str()); - cairo_text_extents_t extents; - cairo_text_extents(*m_context, contents.c_str(), &extents); - - if (!extents.width) { - return; - } - cairo::abspos origin{static_cast(m_rect.x), static_cast(m_rect.y)}; + origin.y += m_rect.height / 2.0; if (m_alignment == alignment::CENTER) { - origin.x += m_rect.width / 2.0 - extents.width / 2.0; - adjust_clickable_areas(extents.width / 2.0); + origin.x += m_rect.width / 2.0; + // adjust_clickable_areas(extents.width / 2.0); } else if (m_alignment == alignment::RIGHT) { - origin.x += m_rect.width - extents.width; - adjust_clickable_areas(extents.width); + origin.x += m_rect.width; + // adjust_clickable_areas(extents.width); } else { origin.x += m_x; } @@ -410,24 +419,16 @@ void renderer::draw_text(const string& contents) { // m_context->restore(); // } - origin.y += m_rect.height / 2.0; - m_context->save(); *m_context << origin; *m_context << m_compositing_foreground; *m_context << m_color_foreground; - *m_context << cairo::textblock{contents, m_fontindex}; + *m_context << cairo::textblock{m_alignment, contents, m_fontindex}; m_context->position(&m_x, &m_y); m_context->restore(); - // if (m_alignment == alignment::CENTER) { - // m_x += extents.width / 2.0; - // } else { - // m_x += extents.width; - // } - - // fill_underline(origin.x, m_x - origin.x); - // fill_overline(origin.x, m_x - origin.x); + fill_underline(origin.x, m_x - origin.x); + fill_overline(origin.x, m_x - origin.x); } /** @@ -450,7 +451,7 @@ void renderer::highlight_clickable_areas() { map hint_num{}; for (auto&& action : m_actions) { if (!action.active) { - unsigned char n = hint_num.find(action.align)->second++; + int n = hint_num.find(action.align)->second++; double x = action.start_x + n * DEBUG_HINTS_OFFSET_X; double y = m_bar.pos.y + m_rect.y + n * DEBUG_HINTS_OFFSET_Y; double w = action.width(); @@ -513,27 +514,28 @@ bool renderer::on(const signals::parser::change_alignment& evt) { } bool renderer::on(const signals::parser::offset_pixel& evt) { - m_x += evt.cast(); + (void)evt; + // m_x += evt.cast(); return true; } bool renderer::on(const signals::parser::attribute_set& evt) { - m_attributes.set(static_cast(evt.cast()), true); + m_attributes.set(static_cast(evt.cast()), true); return true; } bool renderer::on(const signals::parser::attribute_unset& evt) { - m_attributes.set(static_cast(evt.cast()), false); + m_attributes.set(static_cast(evt.cast()), false); return true; } bool renderer::on(const signals::parser::attribute_toggle& evt) { - m_attributes.flip(static_cast(evt.cast())); + m_attributes.flip(static_cast(evt.cast())); return true; } bool renderer::on(const signals::parser::action_begin& evt) { - (void) evt; + (void)evt; // auto a = evt.cast(); // action_block action{}; // action.button = a.button == mousebtn::NONE ? mousebtn::LEFT : a.button; @@ -546,9 +548,9 @@ bool renderer::on(const signals::parser::action_begin& evt) { } bool renderer::on(const signals::parser::action_end& evt) { - (void) evt; + (void)evt; // auto btn = evt.cast(); - // short int clickable_width = 0; + // int clickable_width = 0; // for (auto action = m_actions.rbegin(); action != m_actions.rend(); action++) { // if (action->active && action->align == m_alignment && action->button == btn) { // switch (action->align) { diff --git a/src/x11/tray_client.cpp b/src/x11/tray_client.cpp index a8d0467a..9075083f 100644 --- a/src/x11/tray_client.cpp +++ b/src/x11/tray_client.cpp @@ -101,7 +101,7 @@ void tray_client::reconfigure(int x, int y) const { /** * Respond to client resize requests */ -void tray_client::configure_notify(short int x, short int y) const { +void tray_client::configure_notify(int x, int y) const { auto notify = memory_util::make_malloc_ptr(); notify->response_type = XCB_CONFIGURE_NOTIFY; notify->event = m_window;