From ce93188a4ace3c9eab73ff536533f181abbe5e5c Mon Sep 17 00:00:00 2001
From: Patrick Ziegler
Date: Sun, 20 Feb 2022 21:08:57 +0100
Subject: [PATCH] Add units support (POINT, PIXEL, SPACE) (#2578)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* add units support (POINT, PIXEL, SPACE) for polybar
- add a size_with_unit struct
- add a geometry_format_values struct
- move dpi initialisation from renderer.cpp to bar.cpp
- add a string to size_with_unit converter
- add point support (with pt)
- add pixel support (with px)
* Fix unit test compilation
* clang-format
* Better names
The old names didn't really capture the purpose of the structs and
function.
space_type -> spacing_type
space_size -> spacing_val
size_type -> extent_type
geometry -> extent_val
geometry_format_values -> percentage_with_offset
* Remove parse_size_with_unit
No longer needed. The convert function in config.cpp
already does all the work for us and always setting the type to pixel
was wrong.
In addition, line-size should not be of type spacing_val but extent_val.
* Cleanup
I tried to address most of my comments on the old PR
* Fix renderer width calculation
We can't just blindly add the x difference to the width because for
example the width should increase if x < width and the increase keeps
x < width.
Similarly, we can't just add the offset to the width.
* Rename geom_format_to_pixels to percentage_with_offset_to_pixel
* Cleanup
* Apply suggested changes from Patrick on GitHub
Co-authored-by: Patrick Ziegler
* Update src/components/bar.cpp
Co-authored-by: Patrick Ziegler
* Update src/components/config.cpp
Co-authored-by: Patrick Ziegler
* Update src/components/builder.cpp
Co-authored-by: Patrick Ziegler
* Update src/components/builder.cpp
Co-authored-by: Patrick Ziegler
* config: Use stod for parsing percentage
* Use stof instead of strtof
* units: Fix test edge cases
* Remove unnecessary clang-format toggle
* Use percentage_with_offset for margin-{top,bottom}
* Support negative extent values
* Rename unit to units and create a cpp file
* Move percentage_with_offset_to_pixel unit test to units
* Add unit tests for units_utils
* Clarify when and how negative spacing/extent is allowed
Negative spacing is never allowed and produces a config error.
Extents allow negative values in theory, but only a few use-cases accept
it.
Only the extent value used for the `%{O}` tag and the offset value in
percentage_with_offset can be negative. Everything else is capped below
at 0.
The final pixel value of percentage_with_offset also caps below at 0.
* Fix parsing errors not being caught in config
* Print a proper error message for uncaught exceptions
* Cleanup module::get_output
All changes preserve the existing semantics
* Stop using remove_trailing_space in module::get_output
Instead, we first check if the current tag is built, and only if it is,
the spacing is prepended.
* Remove unused imports
* Restore old behavior
If there are two tags and the second one isn't built (module::build
returns false), the space in between them is removed.
For example in the mpd module:
format-online = foo
If mpd is not running, the mpd module will return false when trying to
build the `` tag. If we don't remove the space between
`` and ``, we end up with two spaces between
`` and `foo`.
This change is to match the old behavior where at least one trailing
space character was removed from the builder.
* Add changelog entry
* Remove unused setting
* Use percentage with offset for tray-offset
Co-authored-by: Jérôme BOULMIER
Co-authored-by: Joe Groocock
---
.clang-format | 1 +
CHANGELOG.md | 5 +-
include/components/bar.hpp | 24 ----
include/components/builder.hpp | 15 ++-
include/components/config.hpp | 4 +-
include/components/controller.hpp | 3 +-
include/components/renderer.hpp | 15 ++-
include/components/renderer_interface.hpp | 2 +-
include/components/types.hpp | 61 ++++++++-
include/drawtypes/label.hpp | 24 ++--
include/modules/cpu.hpp | 3 +-
include/modules/fs.hpp | 2 +-
include/modules/meta/base.hpp | 12 +-
include/modules/meta/base.inl | 117 +++++++++++------
include/tags/parser.hpp | 2 +-
include/tags/types.hpp | 30 ++---
include/utils/color.hpp | 2 +
include/utils/units.hpp | 31 +++++
src/CMakeLists.txt | 1 +
src/components/bar.cpp | 82 +++++++++---
src/components/builder.cpp | 96 ++++++++------
src/components/config.cpp | 35 ++++++
src/components/controller.cpp | 8 +-
src/components/renderer.cpp | 82 ++++++------
src/drawtypes/label.cpp | 43 ++++---
src/main.cpp | 2 +-
src/modules/bspwm.cpp | 2 +-
src/modules/cpu.cpp | 17 ++-
src/modules/fs.cpp | 2 +-
src/modules/menu.cpp | 8 +-
src/modules/meta/base.cpp | 23 ++--
src/modules/xkeyboard.cpp | 2 +-
src/modules/xworkspaces.cpp | 2 +-
src/tags/parser.cpp | 18 +--
src/utils/color.cpp | 8 ++
src/utils/units.cpp | 137 ++++++++++++++++++++
src/x11/tray_manager.cpp | 42 +++----
tests/CMakeLists.txt | 2 +-
tests/unit_tests/components/bar.cpp | 48 -------
tests/unit_tests/tags/dispatch.cpp | 12 +-
tests/unit_tests/tags/parser.cpp | 47 ++++++-
tests/unit_tests/utils/units.cpp | 146 ++++++++++++++++++++++
42 files changed, 860 insertions(+), 358 deletions(-)
create mode 100644 include/utils/units.hpp
create mode 100644 src/utils/units.cpp
delete mode 100644 tests/unit_tests/components/bar.cpp
create mode 100644 tests/unit_tests/utils/units.cpp
diff --git a/.clang-format b/.clang-format
index 3caddce4..4d561f89 100644
--- a/.clang-format
+++ b/.clang-format
@@ -10,4 +10,5 @@ AllowShortIfStatementsOnASingleLine: false
BreakConstructorInitializersBeforeComma: true
DerivePointerAlignment: false
PointerAlignment: Left
+SpacesBeforeTrailingComments: 1
---
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8b789c10..1e28cdc0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -81,6 +81,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `DEBUG_SHADED` cmake variable and its associated functionality.
### Added
+- Support `px` and `pt` units everyhwere where before only a number of spaces
+ or pixels could be specified.
+ ([`#2578`](https://github.com/polybar/polybar/pull/2578))
- Right and middle click events for alsa module.
([`#2566`](https://github.com/polybar/polybar/issues/2566))
- `internal/network`: New token `%mac%` shows MAC address of selected interface
@@ -111,7 +114,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `internal/memory`: `format-warn`, `label-warn`, `warn-percentage = 90`
- `radius` now affects the bar border as well
([`#1566`](https://github.com/polybar/polybar/issues/1566))
-- Per-corner corner radius with `radius-{bottom,top}-{left,right}`
+- Per-corner radius with `radius-{bottom,top}-{left,right}`
([`#2294`](https://github.com/polybar/polybar/issues/2294))
- `internal/network`: `speed-unit = B/s` can be used to customize how network
speeds are displayed.
diff --git a/include/components/bar.hpp b/include/components/bar.hpp
index 83ded49b..43a444dc 100644
--- a/include/components/bar.hpp
+++ b/include/components/bar.hpp
@@ -1,8 +1,6 @@
#pragma once
-#include
#include
-#include
#include "common.hpp"
#include "components/eventloop.hpp"
@@ -31,28 +29,6 @@ namespace tags {
}
// }}}
-/**
- * Allows a new format for pixel sizes (like width in the bar section)
- *
- * The new format is X%:Z, where X is in [0, 100], and Z is any real value
- * describing a pixel offset. The actual value is calculated by X% * max + Z
- */
-inline double geom_format_to_pixels(std::string str, double max) {
- size_t i;
- if ((i = str.find(':')) != std::string::npos) {
- std::string a = str.substr(0, i - 1);
- std::string b = str.substr(i + 1);
- return std::max(
- 0, math_util::percentage_to_value(strtod(a.c_str(), nullptr), max) + strtod(b.c_str(), nullptr));
- } else {
- if (str.find('%') != std::string::npos) {
- return math_util::percentage_to_value(strtod(str.c_str(), nullptr), max);
- } else {
- return strtod(str.c_str(), nullptr);
- }
- }
-}
-
class bar : public xpp::event::sink,
public signal_receiver(move(section), move(key), move(string_value), move(result));
} catch (const key_error& err) {
return default_value;
- } catch (const value_error& err) {
+ } catch (const std::exception& err) {
m_log.err("Invalid value for \"%s.%s\", using default value (reason: %s)", section, key, err.what());
return default_value;
}
@@ -196,7 +196,7 @@ class config {
}
} catch (const key_error& err) {
break;
- } catch (const value_error& err) {
+ } catch (const std::exception& err) {
m_log.err("Invalid value in list \"%s.%s\", using list as-is (reason: %s)", section, key, err.what());
return default_value;
}
diff --git a/include/components/controller.hpp b/include/components/controller.hpp
index ef2d28e9..7c64ae10 100644
--- a/include/components/controller.hpp
+++ b/include/components/controller.hpp
@@ -1,6 +1,5 @@
#pragma once
-#include
#include
#include
@@ -29,7 +28,7 @@ class logger;
class signal_emitter;
namespace modules {
struct module_interface;
-} // namespace modules
+} // namespace modules
using module_t = shared_ptr;
using modulemap_t = std::map>;
// }}}
diff --git a/include/components/renderer.hpp b/include/components/renderer.hpp
index a6fd0b7a..5dfc998a 100644
--- a/include/components/renderer.hpp
+++ b/include/components/renderer.hpp
@@ -28,8 +28,18 @@ using std::map;
struct alignment_block {
cairo_pattern_t* pattern;
+ /**
+ * The x-position where the next thing will be rendered.
+ */
double x;
double y;
+ /**
+ * The total width of this block.
+ *
+ * This is always >= x, but may be larger because a negative offset may
+ * decrease x, but the width doesn't change.
+ */
+ double width;
};
class renderer : public renderer_interface,
@@ -48,7 +58,7 @@ class renderer : public renderer_interface,
void end();
void flush();
- void render_offset(const tags::context& ctxt, int pixels) override;
+ void render_offset(const tags::context& ctxt, const extent_val offset) override;
void render_text(const tags::context& ctxt, const string&&) override;
void change_alignment(const tags::context& ctxt) override;
@@ -62,12 +72,15 @@ class renderer : public renderer_interface,
void fill_overline(rgba color, double x, double w);
void fill_underline(rgba color, double x, double w);
void fill_borders();
+ void draw_offset(rgba color, double x, double w);
double block_x(alignment a) const;
double block_y(alignment a) const;
double block_w(alignment a) const;
double block_h(alignment a) const;
+ void increase_x(double dx);
+
void flush(alignment a);
void highlight_clickable_areas();
diff --git a/include/components/renderer_interface.hpp b/include/components/renderer_interface.hpp
index 84189851..77c718ff 100644
--- a/include/components/renderer_interface.hpp
+++ b/include/components/renderer_interface.hpp
@@ -10,7 +10,7 @@ class renderer_interface {
public:
renderer_interface(const tags::action_context& action_ctxt) : m_action_ctxt(action_ctxt){};
- virtual void render_offset(const tags::context& ctxt, int pixels) = 0;
+ virtual void render_offset(const tags::context& ctxt, const extent_val offset) = 0;
virtual void render_text(const tags::context& ctxt, const string&& str) = 0;
virtual void change_alignment(const tags::context& ctxt) = 0;
diff --git a/include/components/types.hpp b/include/components/types.hpp
index 01c9d72a..69fe0ace 100644
--- a/include/components/types.hpp
+++ b/include/components/types.hpp
@@ -84,9 +84,52 @@ struct size {
unsigned int h{1U};
};
+enum class spacing_type { SPACE, POINT, PIXEL };
+
+enum class extent_type { POINT, PIXEL };
+
+struct spacing_val {
+ spacing_type type{spacing_type::SPACE};
+ /**
+ * Numerical spacing value. Is truncated to an integer for pixels and spaces.
+ * Must be non-negative.
+ */
+ float value{0};
+
+ /**
+ * Any non-positive number is interpreted as no spacing.
+ */
+ operator bool() const {
+ return value > 0;
+ }
+};
+
+static constexpr spacing_val ZERO_SPACE = {spacing_type::SPACE, 0};
+
+/*
+ * Defines the signed length of something as either a number of pixels or points.
+ *
+ * Used for widths, heights, and offsets
+ */
+struct extent_val {
+ extent_type type{extent_type::PIXEL};
+ float value{0};
+
+ operator bool() const {
+ return value != 0;
+ }
+};
+
+static constexpr extent_val ZERO_PX_EXTENT = {extent_type::PIXEL, 0};
+
struct side_values {
- unsigned int left{0U};
- unsigned int right{0U};
+ spacing_val left{ZERO_SPACE};
+ spacing_val right{ZERO_SPACE};
+};
+
+struct percentage_with_offset {
+ double percentage{0};
+ extent_val offset{ZERO_PX_EXTENT};
};
struct edge_values {
@@ -134,11 +177,14 @@ struct bar_settings {
struct size size {
1U, 1U
};
+
+ double dpi_x{0.};
+ double dpi_y{0.};
+
position pos{0, 0};
position offset{0, 0};
- side_values padding{0U, 0U};
- side_values margin{0U, 0U};
- side_values module_margin{0U, 0U};
+ side_values padding{ZERO_SPACE, ZERO_SPACE};
+ side_values module_margin{ZERO_SPACE, ZERO_SPACE};
edge_values strut{0U, 0U, 0U, 0U};
rgba background{0xFF000000};
@@ -151,7 +197,10 @@ struct bar_settings {
std::unordered_map borders{};
struct radius radius {};
- int spacing{0};
+ /**
+ * TODO deprecated
+ */
+ spacing_val spacing{ZERO_SPACE};
label_t separator{};
string wmname{};
diff --git a/include/drawtypes/label.hpp b/include/drawtypes/label.hpp
index b20b6bca..3e178fdf 100644
--- a/include/drawtypes/label.hpp
+++ b/include/drawtypes/label.hpp
@@ -25,8 +25,8 @@ namespace drawtypes {
rgba m_underline{};
rgba m_overline{};
int m_font{0};
- side_values m_padding{0U, 0U};
- side_values m_margin{0U, 0U};
+ side_values m_padding{ZERO_SPACE, ZERO_SPACE};
+ side_values m_margin{ZERO_SPACE, ZERO_SPACE};
size_t m_minlen{0};
/*
@@ -40,15 +40,15 @@ namespace drawtypes {
alignment m_alignment{alignment::LEFT};
bool m_ellipsis{true};
- explicit label(string text, int font) : m_font(font), m_text(text), m_tokenized(m_text) {}
+ explicit label(string text, int font) : m_font(font), m_text(move(text)), m_tokenized(m_text) {}
explicit label(string text, rgba foreground = rgba{}, rgba background = rgba{}, rgba underline = rgba{},
- rgba overline = rgba{}, int font = 0, struct side_values padding = {0U, 0U},
- struct side_values margin = {0U, 0U}, int minlen = 0, size_t maxlen = 0_z,
+ rgba overline = rgba{}, int font = 0, side_values padding = {ZERO_SPACE, ZERO_SPACE},
+ side_values margin = {ZERO_SPACE, ZERO_SPACE}, int minlen = 0, size_t maxlen = 0_z,
alignment label_alignment = alignment::LEFT, bool ellipsis = true, vector&& tokens = {})
- : m_foreground(foreground)
- , m_background(background)
- , m_underline(underline)
- , m_overline(overline)
+ : m_foreground(move(foreground))
+ , m_background(move(background))
+ , m_underline(move(underline))
+ , m_overline(move(overline))
, m_font(font)
, m_padding(padding)
, m_margin(margin)
@@ -56,14 +56,14 @@ namespace drawtypes {
, m_maxlen(maxlen)
, m_alignment(label_alignment)
, m_ellipsis(ellipsis)
- , m_text(text)
+ , m_text(move(text))
, m_tokenized(m_text)
, m_tokens(forward>(tokens)) {
assert(!m_ellipsis || (m_maxlen == 0 || m_maxlen >= 3));
}
string get() const;
- operator bool();
+ explicit operator bool();
label_t clone();
void clear();
void reset_tokens();
@@ -81,6 +81,6 @@ namespace drawtypes {
label_t load_label(const config& conf, const string& section, string name, bool required = true, string def = ""s);
label_t load_optional_label(const config& conf, string section, string name, string def = ""s);
-} // namespace drawtypes
+} // namespace drawtypes
POLYBAR_NS_END
diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp
index 26d7e165..c8cbc75e 100644
--- a/include/modules/cpu.hpp
+++ b/include/modules/cpu.hpp
@@ -40,13 +40,12 @@ namespace modules {
static constexpr auto TAG_RAMP_LOAD_PER_CORE = "";
static constexpr auto FORMAT_WARN = "format-warn";
-
label_t m_label;
label_t m_labelwarn;
progressbar_t m_barload;
ramp_t m_rampload;
ramp_t m_rampload_core;
- int m_ramp_padding;
+ spacing_val m_ramp_padding{spacing_type::SPACE, 1U};
vector m_cputimes;
vector m_cputimes_prev;
diff --git a/include/modules/fs.hpp b/include/modules/fs.hpp
index b43d4ea4..2de51340 100644
--- a/include/modules/fs.hpp
+++ b/include/modules/fs.hpp
@@ -68,7 +68,7 @@ namespace modules {
vector m_mounts;
bool m_fixed{false};
bool m_remove_unmounted{false};
- int m_spacing{2};
+ spacing_val m_spacing{spacing_type::SPACE, 2U};
int m_perc_used_warn{90};
// used while formatting output
diff --git a/include/modules/meta/base.hpp b/include/modules/meta/base.hpp
index 8264a96b..62e00df9 100644
--- a/include/modules/meta/base.hpp
+++ b/include/modules/meta/base.hpp
@@ -36,7 +36,7 @@ namespace drawtypes {
using animation_t = shared_ptr;
class iconset;
using iconset_t = shared_ptr;
-} // namespace drawtypes
+} // namespace drawtypes
class builder;
class config;
@@ -67,10 +67,10 @@ namespace modules {
rgba ol{};
size_t ulsize{0};
size_t olsize{0};
- size_t spacing{0};
- size_t padding{0};
- size_t margin{0};
- int offset{0};
+ spacing_val spacing{ZERO_SPACE};
+ spacing_val padding{ZERO_SPACE};
+ spacing_val margin{ZERO_SPACE};
+ extent_val offset{ZERO_PX_EXTENT};
int font{0};
string decorate(builder* builder, string output);
@@ -225,6 +225,6 @@ namespace modules {
};
// }}}
-} // namespace modules
+} // namespace modules
POLYBAR_NS_END
diff --git a/include/modules/meta/base.inl b/include/modules/meta/base.inl
index 0d3bd82c..1f4378bc 100644
--- a/include/modules/meta/base.inl
+++ b/include/modules/meta/base.inl
@@ -197,48 +197,89 @@ namespace modules {
std::lock_guard guard(m_buildlock);
auto format_name = CONST_MOD(Impl).get_format();
auto format = m_formatter->get(format_name);
- bool no_tag_built{true};
- bool fake_no_tag_built{false};
- bool tag_built{false};
- auto mingap = std::max(1_z, format->spacing);
- size_t start, end;
- string value{format->value};
- while ((start = value.find('<')) != string::npos && (end = value.find('>', start)) != string::npos) {
- if (start > 0) {
- if (no_tag_built) {
- // If no module tag has been built we do not want to add
- // whitespace defined between the format tags, but we do still
- // want to output other non-tag content
- auto trimmed = string_util::ltrim(value.substr(0, start), ' ');
- if (!trimmed.empty()) {
- fake_no_tag_built = false;
- m_builder->node(move(trimmed));
- }
- } else {
- m_builder->node(value.substr(0, start));
+
+ /*
+ * Builder for building individual tags isolated, so that we can
+ */
+ builder tag_builder(m_bar);
+
+ // Whether any tags have been processed yet
+ bool has_tags = false;
+
+ // Cursor pointing into 'value'
+ size_t cursor = 0;
+ const string& value{format->value};
+
+ /*
+ * Search for all tags in the format definition. A tag is enclosed in '<' and '>'.
+ * Each tag is given to the module to produce some output for it. All other text is added as-is.
+ */
+ while (cursor < value.size()) {
+ // Check if there are any tags left
+
+ // Start index of next tag
+ size_t start = value.find('<', cursor);
+
+ if (start == string::npos) {
+ break;
+ }
+
+ // End index (inclusive) of next tag
+ size_t end = value.find('>', start + 1);
+
+ if (end == string::npos) {
+ break;
+ }
+
+ // Potential regular text that appears before the tag.
+ string non_tag;
+
+ // There is some non-tag text
+ if (start > cursor) {
+ /*
+ * Produce anything between the previous and current tag as regular text.
+ */
+ non_tag = value.substr(cursor, start - cursor);
+ if (!has_tags) {
+ /*
+ * If no module tag has been built we do not want to add
+ * whitespace defined between the format tags, but we do still
+ * want to output other non-tag content
+ */
+ non_tag = string_util::ltrim(move(non_tag), ' ');
}
- value.erase(0, start);
- end -= start;
- start = 0;
}
- string tag{value.substr(start, end + 1)};
- if (tag.empty()) {
- continue;
- } else if (tag[0] == '<' && tag[tag.size() - 1] == '>') {
- if (!no_tag_built)
- m_builder->space(format->spacing);
- else if (fake_no_tag_built)
- no_tag_built = false;
- if (!(tag_built = CONST_MOD(Impl).build(m_builder.get(), tag)) && !no_tag_built)
- m_builder->remove_trailing_space(mingap);
- if (tag_built)
- no_tag_built = false;
+
+ string tag = value.substr(start, end - start + 1);
+ bool tag_built = CONST_MOD(Impl).build(&tag_builder, tag);
+ string tag_content = tag_builder.flush();
+
+ /*
+ * Remove exactly one space between two tags if the second tag was not built.
+ */
+ if (!tag_built && has_tags && !format->spacing) {
+ if (!non_tag.empty() && non_tag.back() == ' ') {
+ non_tag.erase(non_tag.size() - 1);
+ }
}
- value.erase(0, tag.size());
+
+ m_builder->node(non_tag);
+
+ if (tag_built) {
+ if (has_tags) {
+ // format-spacing is added between all tags
+ m_builder->spacing(format->spacing);
+ }
+
+ m_builder->append(tag_content);
+ has_tags = true;
+ }
+
+ cursor = end + 1;
}
- if (!value.empty()) {
- m_builder->append(value);
+ if (cursor < value.size()) {
+ m_builder->append(value.substr(cursor));
}
return format->decorate(&*m_builder, m_builder->flush());
@@ -267,6 +308,6 @@ namespace modules {
}
// }}}
-} // namespace modules
+} // namespace modules
POLYBAR_NS_END
diff --git a/include/tags/parser.hpp b/include/tags/parser.hpp
index e2d5229d..ab86394c 100644
--- a/include/tags/parser.hpp
+++ b/include/tags/parser.hpp
@@ -126,7 +126,7 @@ namespace tags {
color_value parse_color();
int parse_fontindex();
- int parse_offset();
+ extent_val parse_offset();
controltag parse_control();
std::pair parse_action();
mousebtn parse_action_btn();
diff --git a/include/tags/types.hpp b/include/tags/types.hpp
index 151ab90b..7ac2ee09 100644
--- a/include/tags/types.hpp
+++ b/include/tags/types.hpp
@@ -13,18 +13,18 @@ namespace tags {
enum class attr_activation { NONE, ON, OFF, TOGGLE };
enum class syntaxtag {
- A, // mouse action
- B, // background color
- F, // foreground color
- T, // font index
- O, // pixel offset
- R, // flip colors
- o, // overline color
- u, // underline color
- P, // Polybar control tag
- l, // Left alignment
- r, // Right alignment
- c, // Center alignment
+ A, // mouse action
+ B, // background color
+ F, // foreground color
+ T, // font index
+ O, // pixel offset
+ R, // flip colors
+ o, // overline color
+ u, // underline color
+ P, // Polybar control tag
+ l, // Left alignment
+ r, // Right alignment
+ c, // Center alignment
};
/**
@@ -35,7 +35,7 @@ namespace tags {
*/
enum class controltag {
NONE = 0,
- R, // Reset all open tags (B, F, T, o, u). Used at module edges
+ R, // Reset all open tags (B, F, T, o, u). Used at module edges
};
enum class color_type { RESET = 0, COLOR };
@@ -89,7 +89,7 @@ namespace tags {
/**
* For for 'O' tags
*/
- int offset;
+ extent_val offset;
/**
* For for 'P' tags
*/
@@ -113,6 +113,6 @@ namespace tags {
using format_string = vector;
-} // namespace tags
+} // namespace tags
POLYBAR_NS_END
diff --git a/include/utils/color.hpp b/include/utils/color.hpp
index c27e679e..3961876f 100644
--- a/include/utils/color.hpp
+++ b/include/utils/color.hpp
@@ -19,7 +19,9 @@ class rgba {
operator string() const;
operator uint32_t() const;
+ operator bool() const;
bool operator==(const rgba& other) const;
+ bool operator!=(const rgba& other) const;
uint32_t value() const;
type get_type() const;
diff --git a/include/utils/units.hpp b/include/utils/units.hpp
new file mode 100644
index 00000000..9066ed42
--- /dev/null
+++ b/include/utils/units.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include "components/types.hpp"
+#include "utils/string.hpp"
+
+POLYBAR_NS
+
+namespace units_utils {
+ int point_to_pixel(double point, double dpi);
+
+ int extent_to_pixel(const extent_val size, double dpi);
+ unsigned extent_to_pixel_nonnegative(const extent_val size, double dpi);
+
+ extent_type parse_extent_unit(const string& str);
+ extent_val parse_extent(const string& str);
+
+ string extent_to_string(extent_val extent);
+
+ unsigned percentage_with_offset_to_pixel(percentage_with_offset g_format, double max, double dpi);
+
+ spacing_type parse_spacing_unit(const string& str);
+ spacing_val parse_spacing(const string& str);
+
+} // namespace units_utils
+
+POLYBAR_NS_END
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1be92e3e..1037f661 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -121,6 +121,7 @@ set(POLY_SOURCES
${src_dir}/utils/process.cpp
${src_dir}/utils/socket.cpp
${src_dir}/utils/string.cpp
+ ${src_dir}/utils/units.cpp
${src_dir}/x11/atoms.cpp
${src_dir}/x11/background_manager.cpp
diff --git a/src/components/bar.cpp b/src/components/bar.cpp
index 6a8b8f15..1fe4f904 100644
--- a/src/components/bar.cpp
+++ b/src/components/bar.cpp
@@ -14,6 +14,7 @@
#include "utils/color.hpp"
#include "utils/math.hpp"
#include "utils/string.hpp"
+#include "utils/units.hpp"
#include "x11/atoms.hpp"
#include "x11/connection.hpp"
#include "x11/ewmh.hpp"
@@ -157,7 +158,39 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
m_opts.wmname = m_conf.get(bs, "wm-name", "polybar-" + bs.substr(4) + "_" + m_opts.monitor->name);
m_opts.wmname = string_util::replace(m_opts.wmname, " ", "-");
+ // Configure DPI
+ {
+ double dpi_x = 96, dpi_y = 96;
+ if (m_conf.has(m_conf.section(), "dpi")) {
+ dpi_x = dpi_y = m_conf.get("dpi");
+ } else {
+ if (m_conf.has(m_conf.section(), "dpi-x")) {
+ dpi_x = m_conf.get("dpi-x");
+ }
+ if (m_conf.has(m_conf.section(), "dpi-y")) {
+ dpi_y = m_conf.get("dpi-y");
+ }
+ }
+
+ // dpi to be computed
+ if (dpi_x <= 0 || dpi_y <= 0) {
+ auto screen = m_connection.screen();
+ if (dpi_x <= 0) {
+ dpi_x = screen->width_in_pixels * 25.4 / screen->width_in_millimeters;
+ }
+ if (dpi_y <= 0) {
+ dpi_y = screen->height_in_pixels * 25.4 / screen->height_in_millimeters;
+ }
+ }
+
+ m_opts.dpi_x = dpi_x;
+ m_opts.dpi_y = dpi_y;
+
+ m_log.info("Configured DPI = %gx%g", dpi_x, dpi_y);
+ }
+
// Load configuration values
+
m_opts.origin = m_conf.get(bs, "bottom", false) ? edge::BOTTOM : edge::TOP;
m_opts.spacing = m_conf.get(bs, "spacing", m_opts.spacing);
m_opts.separator = drawtypes::load_optional_label(m_conf, bs, "separator", "");
@@ -171,11 +204,11 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
m_opts.radius.bottom_left = m_conf.get(bs, "radius-bottom-left", bottom);
m_opts.radius.bottom_right = m_conf.get(bs, "radius-bottom-right", bottom);
- auto padding = m_conf.get(bs, "padding", 0U);
+ auto padding = m_conf.get(bs, "padding", ZERO_SPACE);
m_opts.padding.left = m_conf.get(bs, "padding-left", padding);
m_opts.padding.right = m_conf.get(bs, "padding-right", padding);
- auto margin = m_conf.get(bs, "module-margin", 0U);
+ auto margin = m_conf.get(bs, "module-margin", ZERO_SPACE);
m_opts.module_margin.left = m_conf.get(bs, "module-margin-left", margin);
m_opts.module_margin.right = m_conf.get(bs, "module-margin-right", margin);
@@ -186,8 +219,10 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
}
// Load values used to adjust the struts atom
- m_opts.strut.top = m_conf.get("global/wm", "margin-top", 0);
- m_opts.strut.bottom = m_conf.get("global/wm", "margin-bottom", 0);
+ auto margin_top = m_conf.get("global/wm", "margin-top", percentage_with_offset{});
+ auto margin_bottom = m_conf.get("global/wm", "margin-bottom", percentage_with_offset{});
+ m_opts.strut.top = units_utils::percentage_with_offset_to_pixel(margin_top, m_opts.monitor->h, m_opts.dpi_y);
+ m_opts.strut.bottom = units_utils::percentage_with_offset_to_pixel(margin_bottom, m_opts.monitor->h, m_opts.dpi_y);
// Load commands used for fallback click handlers
vector actions;
@@ -240,44 +275,51 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
// Load over-/underline
auto line_color = m_conf.get(bs, "line-color", rgba{0xFFFF0000});
- auto line_size = m_conf.get(bs, "line-size", 0);
+ auto line_size = m_conf.get(bs, "line-size", ZERO_PX_EXTENT);
- m_opts.overline.size = m_conf.get(bs, "overline-size", line_size);
+ auto overline_size = m_conf.get(bs, "overline-size", line_size);
+ auto underline_size = m_conf.get(bs, "underline-size", line_size);
+
+ m_opts.overline.size = units_utils::extent_to_pixel_nonnegative(overline_size, m_opts.dpi_y);
m_opts.overline.color = parse_or_throw_color("overline-color", line_color);
- m_opts.underline.size = m_conf.get(bs, "underline-size", line_size);
+ m_opts.underline.size = units_utils::extent_to_pixel_nonnegative(underline_size, m_opts.dpi_y);
m_opts.underline.color = parse_or_throw_color("underline-color", line_color);
// Load border settings
auto border_color = m_conf.get(bs, "border-color", rgba{0x00000000});
- auto border_size = m_conf.get(bs, "border-size", ""s);
+ auto border_size = m_conf.get(bs, "border-size", percentage_with_offset{});
auto border_top = m_conf.deprecated(bs, "border-top", "border-top-size", border_size);
auto border_bottom = m_conf.deprecated(bs, "border-bottom", "border-bottom-size", border_size);
auto border_left = m_conf.deprecated(bs, "border-left", "border-left-size", border_size);
auto border_right = m_conf.deprecated(bs, "border-right", "border-right-size", border_size);
m_opts.borders.emplace(edge::TOP, border_settings{});
- m_opts.borders[edge::TOP].size = geom_format_to_pixels(border_top, m_opts.monitor->h);
+ m_opts.borders[edge::TOP].size =
+ units_utils::percentage_with_offset_to_pixel(border_top, m_opts.monitor->h, m_opts.dpi_y);
m_opts.borders[edge::TOP].color = parse_or_throw_color("border-top-color", border_color);
m_opts.borders.emplace(edge::BOTTOM, border_settings{});
- m_opts.borders[edge::BOTTOM].size = geom_format_to_pixels(border_bottom, m_opts.monitor->h);
+ m_opts.borders[edge::BOTTOM].size =
+ units_utils::percentage_with_offset_to_pixel(border_bottom, m_opts.monitor->h, m_opts.dpi_y);
m_opts.borders[edge::BOTTOM].color = parse_or_throw_color("border-bottom-color", border_color);
m_opts.borders.emplace(edge::LEFT, border_settings{});
- m_opts.borders[edge::LEFT].size = geom_format_to_pixels(border_left, m_opts.monitor->w);
+ m_opts.borders[edge::LEFT].size =
+ units_utils::percentage_with_offset_to_pixel(border_left, m_opts.monitor->w, m_opts.dpi_x);
m_opts.borders[edge::LEFT].color = parse_or_throw_color("border-left-color", border_color);
m_opts.borders.emplace(edge::RIGHT, border_settings{});
- m_opts.borders[edge::RIGHT].size = geom_format_to_pixels(border_right, m_opts.monitor->w);
+ m_opts.borders[edge::RIGHT].size =
+ units_utils::percentage_with_offset_to_pixel(border_right, m_opts.monitor->w, m_opts.dpi_x);
m_opts.borders[edge::RIGHT].color = parse_or_throw_color("border-right-color", border_color);
// Load geometry values
- auto w = m_conf.get(m_conf.section(), "width", "100%"s);
- auto h = m_conf.get(m_conf.section(), "height", "24"s);
- auto offsetx = m_conf.get(m_conf.section(), "offset-x", ""s);
- auto offsety = m_conf.get(m_conf.section(), "offset-y", ""s);
+ auto w = m_conf.get(m_conf.section(), "width", percentage_with_offset{100.});
+ auto h = m_conf.get(m_conf.section(), "height", percentage_with_offset{0., {extent_type::PIXEL, 24}});
+ auto offsetx = m_conf.get(m_conf.section(), "offset-x", percentage_with_offset{});
+ auto offsety = m_conf.get(m_conf.section(), "offset-y", percentage_with_offset{});
- m_opts.size.w = geom_format_to_pixels(w, m_opts.monitor->w);
- m_opts.size.h = geom_format_to_pixels(h, m_opts.monitor->h);
- m_opts.offset.x = geom_format_to_pixels(offsetx, m_opts.monitor->w);
- m_opts.offset.y = geom_format_to_pixels(offsety, m_opts.monitor->h);
+ m_opts.size.w = units_utils::percentage_with_offset_to_pixel(w, m_opts.monitor->w, m_opts.dpi_x);
+ m_opts.size.h = units_utils::percentage_with_offset_to_pixel(h, m_opts.monitor->h, m_opts.dpi_y);
+ m_opts.offset.x = units_utils::percentage_with_offset_to_pixel(offsetx, m_opts.monitor->w, m_opts.dpi_x);
+ m_opts.offset.y = units_utils::percentage_with_offset_to_pixel(offsety, m_opts.monitor->h, m_opts.dpi_y);
// Apply offsets
m_opts.pos.x = m_opts.offset.x + m_opts.monitor->x;
diff --git a/src/components/builder.cpp b/src/components/builder.cpp
index 954a15ba..ff313255 100644
--- a/src/components/builder.cpp
+++ b/src/components/builder.cpp
@@ -7,6 +7,8 @@
#include "utils/color.hpp"
#include "utils/string.hpp"
#include "utils/time.hpp"
+#include "utils/units.hpp"
+
POLYBAR_NS
using namespace tags;
@@ -86,9 +88,9 @@ string builder::flush() {
/**
* Insert raw text string
*/
-void builder::append(string text) {
+void builder::append(const string& text) {
m_output.reserve(text.size());
- m_output += move(text);
+ m_output += text;
}
/**
@@ -96,12 +98,12 @@ void builder::append(string text) {
*
* This will also parse raw syntax tags
*/
-void builder::node(string str) {
+void builder::node(const string& str) {
if (str.empty()) {
return;
}
- append(move(str));
+ append(str);
}
/**
@@ -109,9 +111,9 @@ void builder::node(string str) {
*
* \see builder::node
*/
-void builder::node(string str, int font_index) {
+void builder::node(const string& str, int font_index) {
font(font_index);
- node(move(str));
+ node(str);
font_close();
}
@@ -125,8 +127,8 @@ void builder::node(const label_t& label) {
auto text = label->get();
- if (label->m_margin.left > 0) {
- space(label->m_margin.left);
+ if (label->m_margin.left) {
+ spacing(label->m_margin.left);
}
if (label->m_overline.has_color()) {
@@ -143,14 +145,14 @@ void builder::node(const label_t& label) {
color(label->m_foreground);
}
- if (label->m_padding.left > 0) {
- space(label->m_padding.left);
+ if (label->m_padding.left) {
+ spacing(label->m_padding.left);
}
node(text, label->m_font);
- if (label->m_padding.right > 0) {
- space(label->m_padding.right);
+ if (label->m_padding.right) {
+ spacing(label->m_padding.right);
}
if (label->m_background.has_color()) {
@@ -167,8 +169,8 @@ void builder::node(const label_t& label) {
overline_close();
}
- if (label->m_margin.right > 0) {
- space(label->m_margin.right);
+ if (label->m_margin.right) {
+ spacing(label->m_margin.right);
}
}
@@ -200,42 +202,29 @@ void builder::node_repeat(const label_t& label, size_t n) {
}
/**
- * Insert tag that will offset the contents by given pixels
+ * Insert tag that will offset the contents by the given extent
*/
-void builder::offset(int pixels) {
- if (pixels == 0) {
+void builder::offset(extent_val extent) {
+ if (extent) {
return;
}
- tag_open(syntaxtag::O, to_string(pixels));
+ tag_open(syntaxtag::O, units_utils::extent_to_string(extent));
}
/**
- * Insert spaces
+ * Insert spacing
*/
-void builder::space(size_t width) {
- if (width) {
- m_output.append(width, ' ');
- } else {
- space();
+void builder::spacing(spacing_val size) {
+ if (!size && m_bar.spacing) {
+ // TODO remove once the deprecated spacing key in the bar section is removed
+ // The spacing in the bar section acts as a fallback for all spacing value
+ size = m_bar.spacing;
}
-}
-void builder::space() {
- m_output.append(m_bar.spacing, ' ');
-}
-/**
- * Remove trailing space
- */
-void builder::remove_trailing_space(size_t len) {
- if (len == 0_z || len > m_output.size()) {
- return;
- } else if (m_output.substr(m_output.size() - len) == string(len, ' ')) {
- m_output.erase(m_output.size() - len);
+ if (size) {
+ m_output += get_spacing_format_string(size);
}
}
-void builder::remove_trailing_space() {
- remove_trailing_space(m_bar.spacing);
-}
/**
* Insert tag to alter the current font index
@@ -574,4 +563,33 @@ void builder::tag_close(attribute attr) {
}
}
+string builder::get_spacing_format_string(const spacing_val& space) {
+ float value = space.value;
+ if (value == 0) {
+ return "";
+ }
+
+ string out;
+ if (space.type == spacing_type::SPACE) {
+ out += string(value, ' ');
+ } else {
+ out += "%{O";
+
+ switch (space.type) {
+ case spacing_type::POINT:
+ out += to_string(value) + "pt";
+ break;
+ case spacing_type::PIXEL:
+ out += to_string(static_cast(value)) + "px";
+ break;
+ default:
+ break;
+ }
+
+ out += '}';
+ }
+
+ return out;
+}
+
POLYBAR_NS_END
diff --git a/src/components/config.cpp b/src/components/config.cpp
index d72819f1..de30204f 100644
--- a/src/components/config.cpp
+++ b/src/components/config.cpp
@@ -1,13 +1,16 @@
#include "components/config.hpp"
#include
+#include
#include
#include "cairo/utils.hpp"
+#include "components/types.hpp"
#include "utils/color.hpp"
#include "utils/env.hpp"
#include "utils/factory.hpp"
#include "utils/string.hpp"
+#include "utils/units.hpp"
POLYBAR_NS
@@ -217,6 +220,38 @@ unsigned long long config::convert(string&& value) const {
return v < ULLONG_MAX ? v : 0ULL;
}
+template <>
+spacing_val config::convert(string&& value) const {
+ return units_utils::parse_spacing(value);
+}
+
+template <>
+extent_val config::convert(std::string&& value) const {
+ return units_utils::parse_extent(value);
+}
+
+/**
+ * Allows a new format for pixel sizes (like width in the bar section)
+ *
+ * The new format is X%:Z, where X is in [0, 100], and Z is any real value
+ * describing a pixel offset. The actual value is calculated by X% * max + Z
+ */
+template <>
+percentage_with_offset config::convert(string&& value) const {
+ size_t i = value.find(':');
+
+ if (i == std::string::npos) {
+ if (value.find('%') != std::string::npos) {
+ return {std::stod(value), {}};
+ } else {
+ return {0., convert(move(value))};
+ }
+ } else {
+ std::string percentage = value.substr(0, i - 1);
+ return {std::stod(percentage), convert(value.substr(i + 1))};
+ }
+}
+
template <>
chrono::seconds config::convert(string&& value) const {
return chrono::seconds{convert(forward(value))};
diff --git a/src/components/controller.cpp b/src/components/controller.cpp
index fe536fc2..a73eff2e 100644
--- a/src/components/controller.cpp
+++ b/src/components/controller.cpp
@@ -465,10 +465,10 @@ void controller::process_inputdata(string&& cmd) {
bool controller::process_update(bool force) {
const bar_settings& bar{m_bar->settings()};
string contents;
- string padding_left(bar.padding.left, ' ');
- string padding_right(bar.padding.right, ' ');
- string margin_left(bar.module_margin.left, ' ');
- string margin_right(bar.module_margin.right, ' ');
+ string padding_left = builder::get_spacing_format_string(bar.padding.left);
+ string padding_right = builder::get_spacing_format_string(bar.padding.right);
+ string margin_left = builder::get_spacing_format_string(bar.module_margin.left);
+ string margin_right = builder::get_spacing_format_string(bar.module_margin.right);
builder build{bar};
build.node(bar.separator);
diff --git a/src/components/renderer.cpp b/src/components/renderer.cpp
index 94436619..4da65ec9 100644
--- a/src/components/renderer.cpp
+++ b/src/components/renderer.cpp
@@ -8,6 +8,7 @@
#include "events/signal_emitter.hpp"
#include "events/signal_receiver.hpp"
#include "utils/math.hpp"
+#include "utils/units.hpp"
#include "x11/atoms.hpp"
#include "x11/background_manager.hpp"
#include "x11/connection.hpp"
@@ -109,9 +110,9 @@ renderer::renderer(connection& conn, signal_emitter& sig, const config& conf, co
m_log.trace("renderer: Allocate alignment blocks");
{
- m_blocks.emplace(alignment::LEFT, alignment_block{nullptr, 0.0, 0.0});
- m_blocks.emplace(alignment::CENTER, alignment_block{nullptr, 0.0, 0.0});
- m_blocks.emplace(alignment::RIGHT, alignment_block{nullptr, 0.0, 0.0});
+ m_blocks.emplace(alignment::LEFT, alignment_block{nullptr, 0.0, 0.0, 0.});
+ m_blocks.emplace(alignment::CENTER, alignment_block{nullptr, 0.0, 0.0, 0.});
+ m_blocks.emplace(alignment::RIGHT, alignment_block{nullptr, 0.0, 0.0, 0.});
}
m_log.trace("renderer: Allocate cairo components");
@@ -122,31 +123,6 @@ renderer::renderer(connection& conn, signal_emitter& sig, const config& conf, co
m_log.trace("renderer: Load fonts");
{
- double dpi_x = 96, dpi_y = 96;
- if (m_conf.has(m_conf.section(), "dpi")) {
- dpi_x = dpi_y = m_conf.get("dpi");
- } else {
- if (m_conf.has(m_conf.section(), "dpi-x")) {
- dpi_x = m_conf.get("dpi-x");
- }
- if (m_conf.has(m_conf.section(), "dpi-y")) {
- dpi_y = m_conf.get("dpi-y");
- }
- }
-
- // dpi to be computed
- if (dpi_x <= 0 || dpi_y <= 0) {
- auto screen = m_connection.screen();
- if (dpi_x <= 0) {
- dpi_x = screen->width_in_pixels * 25.4 / screen->width_in_millimeters;
- }
- if (dpi_y <= 0) {
- dpi_y = screen->height_in_pixels * 25.4 / screen->height_in_millimeters;
- }
- }
-
- m_log.info("Configured DPI = %gx%g", dpi_x, dpi_y);
-
auto fonts = m_conf.get_list(m_conf.section(), "font", {});
if (fonts.empty()) {
m_log.warn("No fonts specified, using fallback font \"fixed\"");
@@ -161,7 +137,7 @@ renderer::renderer(connection& conn, signal_emitter& sig, const config& conf, co
offset = std::strtol(pattern.substr(pos + 1).c_str(), nullptr, 10);
pattern.erase(pos);
}
- auto font = cairo::make_font(*m_context, string{pattern}, offset, dpi_x, dpi_y);
+ auto font = cairo::make_font(*m_context, string{pattern}, offset, m_bar.dpi_x, m_bar.dpi_y);
m_log.notice("Loaded font \"%s\" (name=%s, offset=%i, file=%s)", pattern, font->name(), offset, font->file());
*m_context << move(font);
}
@@ -287,7 +263,7 @@ void renderer::end() {
// the bar will be filled by the wallpaper creating illusion of transparency.
if (m_pseudo_transparency) {
cairo_pattern_t* barcontents{};
- m_context->pop(&barcontents); // corresponding push is in renderer::begin
+ m_context->pop(&barcontents); // corresponding push is in renderer::begin
auto root_bg = m_background->get_surface();
if (root_bg != nullptr) {
@@ -491,7 +467,7 @@ double renderer::block_y(alignment) const {
* Get block width for given alignment
*/
double renderer::block_w(alignment a) const {
- return m_blocks.at(a).x;
+ return m_blocks.at(a).width;
}
/**
@@ -501,6 +477,14 @@ double renderer::block_h(alignment) const {
return m_rect.height;
}
+void renderer::increase_x(double dx) {
+ m_blocks[m_align].x += dx;
+ /*
+ * The width only increases when x becomes larger than the old width.
+ */
+ m_blocks[m_align].width = std::max(m_blocks[m_align].width, m_blocks[m_align].x);
+}
+
/**
* Fill background color
*/
@@ -695,11 +679,17 @@ void renderer::render_text(const tags::context& ctxt, const string&& contents) {
origin.x = m_rect.x + m_blocks[m_align].x;
origin.y = m_rect.y + m_rect.height / 2.0;
+ double x_old = m_blocks[m_align].x;
+ /*
+ * This variable is increased by the text renderer
+ */
+ double x_new = x_old;
+
cairo::textblock block{};
block.align = m_align;
block.contents = contents;
block.font = ctxt.get_font();
- block.x_advance = &m_blocks[m_align].x;
+ block.x_advance = &x_new;
block.y_advance = &m_blocks[m_align].y;
block.bg_rect = cairo::rect{0.0, 0.0, 0.0, 0.0};
@@ -724,7 +714,9 @@ void renderer::render_text(const tags::context& ctxt, const string&& contents) {
*m_context << block;
m_context->restore();
- double dx = m_rect.x + m_blocks[m_align].x - origin.x;
+ double dx = x_new - x_old;
+ increase_x(dx);
+
if (dx > 0.0) {
if (ctxt.has_underline()) {
fill_underline(ctxt.get_ul(), origin.x, dx);
@@ -736,9 +728,26 @@ void renderer::render_text(const tags::context& ctxt, const string&& contents) {
}
}
-void renderer::render_offset(const tags::context&, int pixels) {
- m_log.trace_x("renderer: offset_pixel(%f)", pixels);
- m_blocks[m_align].x += pixels;
+void renderer::draw_offset(rgba color, double x, double w) {
+ if (w > 0 && color != m_bar.background) {
+ m_log.trace_x("renderer: offset(x=%f, w=%f)", x, w);
+ m_context->save();
+ *m_context << m_comp_bg;
+ *m_context << color;
+ *m_context << cairo::rect{
+ m_rect.x + x, static_cast(m_rect.y), w, static_cast(m_rect.y + m_rect.height)};
+ m_context->fill();
+ m_context->restore();
+ }
+}
+
+void renderer::render_offset(const tags::context& ctxt, const extent_val offset) {
+ m_log.trace_x("renderer: offset_pixel(%f)", offset);
+
+ int offset_width = units_utils::extent_to_pixel(offset, m_bar.dpi_x);
+ rgba bg = ctxt.get_bg();
+ draw_offset(bg, m_blocks[m_align].x, offset_width);
+ increase_x(offset_width);
}
void renderer::change_alignment(const tags::context& ctxt) {
@@ -754,6 +763,7 @@ void renderer::change_alignment(const tags::context& ctxt) {
m_align = align;
m_blocks[m_align].x = 0.0;
m_blocks[m_align].y = 0.0;
+ m_blocks[m_align].width = 0.;
m_context->push();
m_log.trace_x("renderer: push(%i)", static_cast(m_align));
diff --git a/src/drawtypes/label.cpp b/src/drawtypes/label.cpp
index 6dbdd680..e4317d9b 100644
--- a/src/drawtypes/label.cpp
+++ b/src/drawtypes/label.cpp
@@ -113,16 +113,16 @@ namespace drawtypes {
if (label->m_font != 0) {
m_font = label->m_font;
}
- if (label->m_padding.left != 0U) {
+ if (label->m_padding.left) {
m_padding.left = label->m_padding.left;
}
- if (label->m_padding.right != 0U) {
+ if (label->m_padding.right) {
m_padding.right = label->m_padding.right;
}
- if (label->m_margin.left != 0U) {
+ if (label->m_margin.left) {
m_margin.left = label->m_margin.left;
}
- if (label->m_margin.right != 0U) {
+ if (label->m_margin.right) {
m_margin.right = label->m_margin.right;
}
if (label->m_maxlen != 0_z) {
@@ -147,16 +147,16 @@ namespace drawtypes {
if (m_font == 0 && label->m_font != 0) {
m_font = label->m_font;
}
- if (m_padding.left == 0U && label->m_padding.left != 0U) {
+ if (!m_padding.left && label->m_padding.left) {
m_padding.left = label->m_padding.left;
}
- if (m_padding.right == 0U && label->m_padding.right != 0U) {
+ if (!m_padding.right && label->m_padding.right) {
m_padding.right = label->m_padding.right;
}
- if (m_margin.left == 0U && label->m_margin.left != 0U) {
+ if (!m_margin.left && label->m_margin.left) {
m_margin.left = label->m_margin.left;
}
- if (m_margin.right == 0U && label->m_margin.right != 0U) {
+ if (!m_margin.right && label->m_margin.right) {
m_margin.right = label->m_margin.right;
}
if (m_maxlen == 0_z && label->m_maxlen != 0_z) {
@@ -182,14 +182,23 @@ namespace drawtypes {
if (required) {
text = conf.get(section, name);
} else {
- text = conf.get(section, name, move(def));
+ text = conf.get(section, name, def);
}
- const auto get_left_right = [&](string key) {
- auto value = conf.get(section, key, 0U);
- auto left = conf.get(section, key + "-left", value);
- auto right = conf.get(section, key + "-right", value);
- return side_values{static_cast(left), static_cast(right)};
+ const auto get_left_right = [&](string&& key) {
+ const auto parse_or_throw = [&](const string& key, spacing_val default_value) {
+ try {
+ return conf.get(section, key, default_value);
+ } catch (const std::exception& err) {
+ throw application_error(
+ sstream() << "Failed to set " << section << "." << key << " (reason: " << err.what() << ")");
+ }
+ };
+
+ auto value = parse_or_throw(key, ZERO_SPACE);
+ auto left = parse_or_throw(key + "-left", value);
+ auto right = parse_or_throw(key + "-right", value);
+ return side_values{left, right};
};
padding = get_left_right(name + "-padding");
@@ -271,10 +280,12 @@ namespace drawtypes {
}
bool ellipsis = conf.get(section, name + "-ellipsis", true);
+ // clang-format off
if (ellipsis && maxlen > 0 && maxlen < 3) {
throw application_error(sstream() << "Label " << section << "." << name << " has maxlen " << maxlen
<< ", which is smaller than length of ellipsis (3)");
}
+ // clang-format on
// clang-format off
return std::make_shared