From f37ecfe95991d2d7859bae0d3532ec3d87fb3f40 Mon Sep 17 00:00:00 2001
From: Michael Carlberg <c@rlberg.se>
Date: Wed, 25 Jan 2017 15:00:34 +0100
Subject: [PATCH] fix(renderer): Falloff gradient

---
 include/cairo/context.hpp   |   6 +++
 src/components/renderer.cpp | 101 ++++++++++++++++++++++--------------
 2 files changed, 69 insertions(+), 38 deletions(-)

diff --git a/include/cairo/context.hpp b/include/cairo/context.hpp
index b1a7f748..a10bf3fa 100644
--- a/include/cairo/context.hpp
+++ b/include/cairo/context.hpp
@@ -315,6 +315,12 @@ namespace cairo {
       return *this;
     }
 
+    context& clip() {
+      cairo_clip(m_c);
+      cairo_new_path(m_c);
+      return *this;
+    }
+
     context& clip(const rect& r) {
       *this << r;
       cairo_clip(m_c);
diff --git a/src/components/renderer.cpp b/src/components/renderer.cpp
index 64fb13ff..9a136ac8 100644
--- a/src/components/renderer.cpp
+++ b/src/components/renderer.cpp
@@ -13,6 +13,8 @@
 
 POLYBAR_NS
 
+static constexpr double BLOCK_GAP{20.0};
+
 /**
  * Create instance
  */
@@ -210,11 +212,11 @@ void renderer::begin() {
   m_ol = m_bar.overline.color;
 
   // Clear canvas
-  m_context->clear();
   m_context->save();
+  m_context->clear();
 
   // Create corner mask
-  if (m_bar.radius != 0.0) {
+  if (m_bar.radius != 0.0 && m_cornermask == nullptr) {
     m_context->save();
     m_context->push();
     // clang-format off
@@ -249,28 +251,21 @@ void renderer::begin() {
 void renderer::end() {
   m_log.trace_x("renderer: end");
 
-  for (auto&& b : m_blocks) {
-    if (block_x(b.first) + block_w(b.first) > (m_rect.width)) {
-      double x = m_rect.x + block_x(b.first) + block_w(b.first);
-      if ((x -= x - m_rect.x - m_rect.width + block_x(b.first)) > 0.0) {
-        // clang-format off
-        rgba bg1{m_bar.background}; bg1.a = 0.0;
-        rgba bg2{m_bar.background}; bg2.a = 1.0;
-        // clang-format on
-        m_context->save();
-        *m_context << cairo::linear_gradient{x - 40.0, 0.0, x, 0.0, {bg1, bg2}};
-        m_context->paint();
-        m_context->restore();
-      }
-    }
-  }
-
   for (auto&& a : m_actions) {
     a.start_x += block_x(a.align) + m_rect.x;
     a.end_x += block_x(a.align) + m_rect.x;
   }
 
-  flush(m_align);
+  if (m_align != alignment::NONE) {
+    m_log.trace_x("renderer: pop(%i)", static_cast<int>(m_align));
+    m_context->pop(&m_blocks[m_align].pattern);
+
+    for (auto&& b : m_blocks) {
+      if (block_w(b.first) != 0.0) {
+        flush(b.first);
+      }
+    }
+  }
 
   m_context->restore();
   m_surface->flush();
@@ -282,27 +277,50 @@ void renderer::end() {
  * Flush contents of given alignment block
  */
 void renderer::flush(alignment a) {
-  if (a != alignment::NONE) {
-    double x = block_x(a);
-    double y = block_y(a);
-    double w = block_w(a);
-    double h = block_h(a);
+  m_log.trace_x("renderer: flush(%i)", static_cast<int>(a));
 
-    m_surface->flush();
-    m_context->pop(&m_blocks[m_align].pattern);
+  double x = block_x(a);
+  double y = block_y(a);
+  double w = block_w(a);
+  double h = block_h(a);
+  double xw = x + w;
 
-    m_context->save();
-    {
-      *m_context << cairo::rect{m_rect.x + x, m_rect.y + y, w, h};
-      *m_context << cairo::translate{x, 0.0};
-      *m_context << m_blocks[a].pattern;
+  m_surface->flush();
+  m_context->save();
+  {
+    *m_context << cairo::abspos{0.0, 0.0};
+    *m_context << cairo::rect{m_rect.x + x, m_rect.y + y, w, h};
+    *m_context << cairo::translate{x, 0.0};
+    *m_context << m_blocks[a].pattern;
+
+    if (xw <= m_rect.width) {
+      m_context->paint();
+    } else {
+      double sx = w - (xw - m_rect.width);
+      vector<unsigned int> falloff{0xFFFFFFFF, 0x00FFFFFF};
+      cairo_pattern_t* pattern{};
+
+      m_context->push();
+      *m_context << cairo::linear_gradient{sx - 40.0, 0.0, sx, 0.0, falloff};
+      *m_context << CAIRO_OPERATOR_SOURCE;
+      m_context->paint();
+      m_context->pop(&pattern);
+      m_context->mask(pattern);
+      m_context->destroy(&pattern);
+
+      // clang-format off
+      rgba bg1{m_bar.background}; bg1.a = 0.0;
+      rgba bg2{m_bar.background}; bg2.a = 0.4;
+      // clang-format on
+      *m_context << cairo::linear_gradient{sx - 40.0, 0.0, sx, 0.0, {bg1, bg2}};
+      *m_context << CAIRO_OPERATOR_OVER;
       m_context->paint();
-      m_surface->flush();
     }
-    m_context->restore();
+
     m_surface->flush();
-    m_context->destroy(&m_blocks[a].pattern);
   }
+  m_context->restore();
+  m_context->destroy(&m_blocks[a].pattern);
 }
 
 /**
@@ -354,12 +372,12 @@ double renderer::block_x(alignment a) const {
     case alignment::CENTER:
       if (!m_fixedcenter || m_rect.width / 2.0 + block_w(a) / 2.0 > m_rect.width - block_w(alignment::RIGHT)) {
         return std::max((m_rect.width - block_w(alignment::RIGHT) + block_w(alignment::LEFT)) / 2.0 - block_w(a) / 2.0,
-            block_w(alignment::LEFT));
+            block_w(alignment::LEFT) + BLOCK_GAP);
       } else {
         return std::max(m_rect.width / 2.0 - block_w(a) / 2.0, block_w(alignment::LEFT));
       }
     case alignment::RIGHT:
-      return std::max(m_rect.width - block_w(a), block_x(alignment::CENTER) + block_w(alignment::CENTER));
+      return std::max(m_rect.width - block_w(a), block_x(alignment::CENTER) + BLOCK_GAP + block_w(alignment::CENTER));
     default:
       return 0.0;
   }
@@ -634,11 +652,18 @@ bool renderer::on(const signals::parser::change_alignment& evt) {
   auto align = static_cast<const alignment&>(evt.cast());
   if (align != m_align) {
     m_log.trace_x("renderer: change_alignment(%i)", static_cast<int>(align));
-    flush(m_align);
-    m_context->push();
+
+    if (m_align != alignment::NONE) {
+      m_log.trace_x("renderer: pop(%i)", static_cast<int>(m_align));
+      m_context->pop(&m_blocks[m_align].pattern);
+    }
+
+    // flush(m_align);
     m_align = align;
     m_blocks[m_align].x = 0.0;
     m_blocks[m_align].y = 0.0;
+    m_context->push();
+    m_log.trace_x("renderer: push(%i)", static_cast<int>(m_align));
   }
   return true;
 }